summaryrefslogtreecommitdiff
path: root/storage/tokudb/PerconaFT/src
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2015-10-26 12:48:26 +0100
committerSergei Golubchik <serg@mariadb.org>2015-10-26 12:57:57 +0100
commit2c8c65297865d9f8da501761f46e2a34e29af603 (patch)
tree3fdf4a00f8537bb3564827884f923ac56966e778 /storage/tokudb/PerconaFT/src
downloadmariadb-git-2c8c65297865d9f8da501761f46e2a34e29af603.tar.gz
5.6.26-74.0
Diffstat (limited to 'storage/tokudb/PerconaFT/src')
-rw-r--r--storage/tokudb/PerconaFT/src/CMakeLists.txt56
-rw-r--r--storage/tokudb/PerconaFT/src/errors.cc141
-rw-r--r--storage/tokudb/PerconaFT/src/export.map98
-rw-r--r--storage/tokudb/PerconaFT/src/indexer-internal.h116
-rw-r--r--storage/tokudb/PerconaFT/src/indexer-undo-do.cc652
-rw-r--r--storage/tokudb/PerconaFT/src/indexer.cc715
-rw-r--r--storage/tokudb/PerconaFT/src/indexer.h125
-rw-r--r--storage/tokudb/PerconaFT/src/loader.cc518
-rw-r--r--storage/tokudb/PerconaFT/src/loader.h156
-rw-r--r--storage/tokudb/PerconaFT/src/tests/CMakeLists.txt491
-rw-r--r--storage/tokudb/PerconaFT/src/tests/big-nested-abort-abort.cc151
-rw-r--r--storage/tokudb/PerconaFT/src/tests/big-nested-abort-commit.cc149
-rw-r--r--storage/tokudb/PerconaFT/src/tests/big-nested-commit-abort.cc144
-rw-r--r--storage/tokudb/PerconaFT/src/tests/big-nested-commit-commit.cc145
-rw-r--r--storage/tokudb/PerconaFT/src/tests/big-shutdown.cc136
-rw-r--r--storage/tokudb/PerconaFT/src/tests/bigtxn27.cc172
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blackhole.cc129
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-first-empty.cc181
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-first.cc201
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-last.cc201
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-next-prev-deadlock.cc266
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-next-prev.cc272
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-prelock-range.cc158
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-put-timeout.cc189
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-put-wakeup.cc184
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-put.cc157
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-set-range-0.cc213
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-set-range-n.cc206
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-set-range-reverse-0.cc211
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-set.cc200
-rw-r--r--storage/tokudb/PerconaFT/src/tests/blocking-table-lock.cc151
-rw-r--r--storage/tokudb/PerconaFT/src/tests/bug1381.cc189
-rw-r--r--storage/tokudb/PerconaFT/src/tests/cachetable-race.cc152
-rw-r--r--storage/tokudb/PerconaFT/src/tests/checkpoint1.cc94
-rw-r--r--storage/tokudb/PerconaFT/src/tests/checkpoint_fairness.cc133
-rw-r--r--storage/tokudb/PerconaFT/src/tests/checkpoint_stress.cc375
-rw-r--r--storage/tokudb/PerconaFT/src/tests/checkpoint_test.h484
-rw-r--r--storage/tokudb/PerconaFT/src/tests/create-datadir.cc118
-rw-r--r--storage/tokudb/PerconaFT/src/tests/cursor-isolation.cc136
-rw-r--r--storage/tokudb/PerconaFT/src/tests/cursor-more-than-a-leaf-provdel.cc145
-rw-r--r--storage/tokudb/PerconaFT/src/tests/cursor-set-del-rmw.cc146
-rw-r--r--storage/tokudb/PerconaFT/src/tests/cursor-set-range-rmw.cc160
-rw-r--r--storage/tokudb/PerconaFT/src/tests/cursor-step-over-delete.cc108
-rw-r--r--storage/tokudb/PerconaFT/src/tests/db-put-simple-deadlock-threads.cc241
-rw-r--r--storage/tokudb/PerconaFT/src/tests/db-put-simple-deadlock.cc158
-rw-r--r--storage/tokudb/PerconaFT/src/tests/db-put-simple-lockwait.cc189
-rw-r--r--storage/tokudb/PerconaFT/src/tests/db-put-update-deadlock.cc237
-rw-r--r--storage/tokudb/PerconaFT/src/tests/dbremove-nofile-limit.cc125
-rw-r--r--storage/tokudb/PerconaFT/src/tests/del-multiple-huge-primary-row.cc240
-rw-r--r--storage/tokudb/PerconaFT/src/tests/del-multiple-srcdb.cc235
-rw-r--r--storage/tokudb/PerconaFT/src/tests/del-multiple.cc236
-rw-r--r--storage/tokudb/PerconaFT/src/tests/del-simple.cc152
-rw-r--r--storage/tokudb/PerconaFT/src/tests/directory_lock.cc390
-rw-r--r--storage/tokudb/PerconaFT/src/tests/diskfull.cc254
-rw-r--r--storage/tokudb/PerconaFT/src/tests/drd.suppressions107
-rw-r--r--storage/tokudb/PerconaFT/src/tests/dump-env.cc128
-rw-r--r--storage/tokudb/PerconaFT/src/tests/env-put-multiple.cc322
-rw-r--r--storage/tokudb/PerconaFT/src/tests/env_loader_memory.cc62
-rw-r--r--storage/tokudb/PerconaFT/src/tests/env_nproc.cc90
-rw-r--r--storage/tokudb/PerconaFT/src/tests/env_startup.cc197
-rw-r--r--storage/tokudb/PerconaFT/src/tests/filesize.cc269
-rw-r--r--storage/tokudb/PerconaFT/src/tests/get_key_after_bytes_unit.cc247
-rw-r--r--storage/tokudb/PerconaFT/src/tests/get_last_key.cc247
-rw-r--r--storage/tokudb/PerconaFT/src/tests/helgrind.suppressions158
-rw-r--r--storage/tokudb/PerconaFT/src/tests/helgrind1.cc65
-rw-r--r--storage/tokudb/PerconaFT/src/tests/helgrind2.cc135
-rw-r--r--storage/tokudb/PerconaFT/src/tests/helgrind3.cc135
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hot-optimize-table-tests.cc239
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-bw.cc469
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-error-callback.cc172
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-insert-committed-optimized.cc183
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-insert-committed.cc181
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-insert-provisional.cc182
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-lock-test.cc220
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-multiclient.cc466
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-nested-insert-committed.cc187
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-put-abort.cc188
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-put-commit.cc214
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-put-multiple.cc225
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-simple-abort-put.cc130
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-simple-abort.cc118
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-test.cc596
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/README77
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.d200.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.d200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.i200.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.i200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.d200.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.d200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.i200.result6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.i200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.d200.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.d200.test0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.i200.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.i200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.d200.result7
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.d200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.i200.result9
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.i200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.result1
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/insert.300.result9
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/insert.300.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.live.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.live.test7
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.result1
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.1.live.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.1.live.test7
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.live.result6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.live.test8
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.test7
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.3.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.3.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov-2.py45
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.live.result15
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.live.test8
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.result14
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.test7
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.d0.i100.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.d0.i100.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.i100.i200.result1
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.i100.i200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.d0.i100.result1
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.d0.i100.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.i100.i200.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.i100.i200.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d100.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d100.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.d201.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.d201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.i201.result1
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.i201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i100.result1
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i100.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.d201.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.d201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.i201.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.i201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.d201.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.d201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.i201.result1
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.i201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d100.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d100.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.d201.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.d201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.i201.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.i201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i100.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i100.test3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.d201.result6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.d201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.i201.result7
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.i201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.d201.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.d201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.i201.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.i201.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.d100.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.d100.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.i100.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.i100.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.d100.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.d100.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.i100.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.i100.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.d201.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.d201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.i201.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.i201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.d201.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.d201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.i201.result6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.i201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.d201.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.d201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.i201.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.i201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.d201.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.d201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.i201.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.i201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.d201.result7
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.d201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.i201.result9
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.i201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.d201.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.d201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.i201.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.i201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.commit202.i0.i200.i201.i202.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.commit202.i0.i200.i201.i202.test8
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.i0.i200.i201.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.i0.i200.i201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.committing201.i0.i200.i201.result9
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.committing201.i0.i200.i201.test6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.d201.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.d201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.i201.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.i201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.d201.result4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.d201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.i201.result6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.i201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.d201.result0
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.d201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.i201.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.i201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.d201.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.d201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.i201.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.i201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.d201.result7
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.d201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.i201.result9
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.i201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.d201.result3
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.d201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.i201.result5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.i201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i100.result2
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i100.test4
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i200.i201.result6
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i200.i201.test5
-rw-r--r--storage/tokudb/PerconaFT/src/tests/hotindexer-with-queries.cc275
-rw-r--r--storage/tokudb/PerconaFT/src/tests/inflate.cc171
-rw-r--r--storage/tokudb/PerconaFT/src/tests/inflate2.cc161
-rw-r--r--storage/tokudb/PerconaFT/src/tests/insert-dup-prelock.cc171
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/ipm.py61
-rw-r--r--storage/tokudb/PerconaFT/src/tests/isolation-read-committed.cc161
-rw-r--r--storage/tokudb/PerconaFT/src/tests/isolation.cc94
-rw-r--r--storage/tokudb/PerconaFT/src/tests/key-val.h245
-rw-r--r--storage/tokudb/PerconaFT/src/tests/keyrange-merge.cc234
-rw-r--r--storage/tokudb/PerconaFT/src/tests/keyrange.cc336
-rw-r--r--storage/tokudb/PerconaFT/src/tests/last-verify-time.cc150
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-cleanup-test.cc1067
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-close-nproc-limit.cc143
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-create-abort.cc118
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-create-close.cc130
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-create-commit-nproc-limit.cc159
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-create-nproc-limit.cc147
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-dup-test.cc452
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-no-puts.cc245
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-reference-test.cc254
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-stress-del.cc733
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-stress-test.cc697
-rw-r--r--storage/tokudb/PerconaFT/src/tests/loader-tpch-load.cc508
-rw-r--r--storage/tokudb/PerconaFT/src/tests/locktree_escalation_stalls.cc259
-rw-r--r--storage/tokudb/PerconaFT/src/tests/manyfiles.cc123
-rw-r--r--storage/tokudb/PerconaFT/src/tests/maxsize-for-loader.cc392
-rw-r--r--storage/tokudb/PerconaFT/src/tests/medium-nested-commit-commit.cc152
-rw-r--r--storage/tokudb/PerconaFT/src/tests/multiprocess.cc234
-rw-r--r--storage/tokudb/PerconaFT/src/tests/mvcc-create-table.cc88
-rw-r--r--storage/tokudb/PerconaFT/src/tests/mvcc-many-committed.cc138
-rw-r--r--storage/tokudb/PerconaFT/src/tests/mvcc-read-committed.cc96
-rw-r--r--storage/tokudb/PerconaFT/src/tests/openlimit17-locktree.cc117
-rw-r--r--storage/tokudb/PerconaFT/src/tests/openlimit17-metafiles.cc105
-rw-r--r--storage/tokudb/PerconaFT/src/tests/openlimit17.cc100
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_checkpoint_var.cc142
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_child_txn.cc90
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_cursor_nop.cc81
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_iibench.cc453
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_insert.cc91
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_malloc_free.cc81
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_nop.cc77
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_partitioned_counter.cc105
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_ptquery.cc102
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_ptquery2.cc115
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_rangequery.cc71
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_read_txn.cc84
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_read_txn_single_thread.cc110
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_read_write.cc117
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_root_txn.cc83
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_simple_counter.cc79
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_thread_counter.cc79
-rw-r--r--storage/tokudb/PerconaFT/src/tests/perf_txn_single_thread.cc110
-rw-r--r--storage/tokudb/PerconaFT/src/tests/powerfail.cc186
-rw-r--r--storage/tokudb/PerconaFT/src/tests/preload-db-nested.cc340
-rw-r--r--storage/tokudb/PerconaFT/src/tests/preload-db.cc246
-rw-r--r--storage/tokudb/PerconaFT/src/tests/prelock-read-read.cc112
-rw-r--r--storage/tokudb/PerconaFT/src/tests/prelock-read-write.cc106
-rw-r--r--storage/tokudb/PerconaFT/src/tests/prelock-write-read.cc106
-rw-r--r--storage/tokudb/PerconaFT/src/tests/prelock-write-write.cc106
-rw-r--r--storage/tokudb/PerconaFT/src/tests/print_engine_status.cc177
-rw-r--r--storage/tokudb/PerconaFT/src/tests/progress.cc445
-rw-r--r--storage/tokudb/PerconaFT/src/tests/put-del-multiple-array-indexing.cc371
-rw-r--r--storage/tokudb/PerconaFT/src/tests/queries_with_deletes.cc196
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-2483.cc201
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-3113.cc178
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-5146.cc180
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fcreate-fdelete-fcreate.cc165
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fopen-abort.cc249
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fopen-commit.cc249
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-child-rollback.cc117
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-compare-db-descriptor.cc330
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-compare-db.cc306
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-del-multiple-abort.cc285
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-del-multiple-srcdb-fdelete-all.cc285
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-del-multiple.cc277
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-delboth-after-checkpoint.cc247
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-delboth-checkpoint.cc247
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor.cc185
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor10.cc201
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor11.cc191
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor12.cc191
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor2.cc188
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor3.cc188
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor4.cc187
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor5.cc187
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor6.cc187
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor7.cc199
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor8.cc201
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-descriptor9.cc199
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fassociate.cc165
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fclose-in-checkpoint.cc157
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fcreate-basementnodesize.cc192
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fcreate-fclose.cc147
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fcreate-fdelete.cc156
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fcreate-nodesize.cc193
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fcreate-xabort.cc143
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt1.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt10.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt2.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt3.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt4.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt5.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt6.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt7.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt8.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-flt9.cc58
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fopen-checkpoint-fclose.cc153
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fopen-fclose-checkpoint.cc153
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-fopen-fdelete-checkpoint-fcreate.cc187
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-hotindexer-simple-abort-put.cc146
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-loader-test.cc518
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-lsn-filter-multiple.cc248
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-lsn-filter.cc190
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-missing-dbfile-2.cc186
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-missing-dbfile.cc177
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-missing-logfile.cc182
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-put-multiple-abort.cc257
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-put-multiple-fdelete-all.cc232
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-put-multiple-fdelete-some.cc251
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-put-multiple-srcdb-fdelete-all.cc233
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-put-multiple.cc277
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-rollback.cc209
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-rollinclude.cc221
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-split-checkpoint.cc198
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-straddle-txn-nested.cc172
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-straddle-txn.cc176
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-tablelock.cc239
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test-logsuppress-put.cc290
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test-logsuppress.cc287
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test1.cc160
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test2.cc180
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test3.cc188
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test_crash_in_flusher_thread.h135
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test_stress1.cc151
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test_stress2.cc84
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test_stress3.cc180
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-test_stress_openclose.cc63
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update-multiple-abort.cc497
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update-multiple.cc507
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_aborts.cc215
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_aborts_before_checkpoint.cc215
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_aborts_before_close.cc215
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts.cc206
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts2.cc208
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts3.cc208
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts_before_checkpoint.cc206
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts_before_close.cc206
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values.cc210
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values2.cc213
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values3.cc211
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values_before_checkpoint.cc207
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values_before_close.cc207
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_changes_values.cc216
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_changes_values_before_checkpoint.cc216
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-update_changes_values_before_close.cc216
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-upgrade-db-descriptor-multihandle.cc327
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-upgrade-db-descriptor.cc330
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-x1-abort.cc304
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-x1-commit.cc307
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-x1-nested-abort.cc290
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-x1-nested-commit.cc291
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-x2-abort.cc267
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recover-x2-commit.cc267
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recovery_fileops_stress.cc587
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recovery_fileops_unit.cc643
-rw-r--r--storage/tokudb/PerconaFT/src/tests/recovery_stress.cc584
-rw-r--r--storage/tokudb/PerconaFT/src/tests/redirect.cc327
-rw-r--r--storage/tokudb/PerconaFT/src/tests/replace-into-write-lock.cc102
-rw-r--r--storage/tokudb/PerconaFT/src/tests/root_fifo_1.cc185
-rw-r--r--storage/tokudb/PerconaFT/src/tests/root_fifo_2.cc166
-rw-r--r--storage/tokudb/PerconaFT/src/tests/root_fifo_31.cc188
-rw-r--r--storage/tokudb/PerconaFT/src/tests/root_fifo_32.cc188
-rw-r--r--storage/tokudb/PerconaFT/src/tests/root_fifo_41.cc230
-rw-r--r--storage/tokudb/PerconaFT/src/tests/rowsize.cc91
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run-hotindexer-undo-do-tests.bash50
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_abortrecover_test.sh19
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_checkpoint_stress_test.sh26
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_diskfull_test.sh20
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_powerfail_test.py140
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_recover_stress_test.sh27
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_recover_test.sh29
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_recovery_fileops_unit.sh20
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_stress_test.py34
-rwxr-xr-xstorage/tokudb/PerconaFT/src/tests/run_test_thread_stack.sh14
-rw-r--r--storage/tokudb/PerconaFT/src/tests/seqinsert.cc112
-rw-r--r--storage/tokudb/PerconaFT/src/tests/shutdown-3344.cc232
-rw-r--r--storage/tokudb/PerconaFT/src/tests/simple.cc89
-rw-r--r--storage/tokudb/PerconaFT/src/tests/stat64-create-modify-times.cc129
-rw-r--r--storage/tokudb/PerconaFT/src/tests/stat64-null-txn.cc173
-rw-r--r--storage/tokudb/PerconaFT/src/tests/stat64-root-changes.cc249
-rw-r--r--storage/tokudb/PerconaFT/src/tests/stat64.cc168
-rw-r--r--storage/tokudb/PerconaFT/src/tests/stress-gc.cc115
-rw-r--r--storage/tokudb/PerconaFT/src/tests/stress-gc2.cc81
-rw-r--r--storage/tokudb/PerconaFT/src/tests/stress-test.cc264
-rw-r--r--storage/tokudb/PerconaFT/src/tests/stress_openclose.h287
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test-5138.cc87
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test-nested-xopen-eclose.cc142
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test-prepare.cc139
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test-prepare2.cc161
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test-prepare3.cc339
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test-rollinclude.cc118
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test-xa-prepare.cc157
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test-xopen-eclose.cc139
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test.h454
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test1572.cc112
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test1753.cc90
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test1842.cc177
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test3039.cc278
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test3219.cc207
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test3522.cc178
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test3522b.cc189
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test3529.cc210
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test4573-logtrim.cc121
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test5092.cc81
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test938.cc186
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test938b.cc113
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test938c.cc120
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_3529_insert_2.cc219
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc212
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_3645.cc322
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_3755.cc156
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_4015.cc171
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_4368.cc71
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_4657.cc133
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_5015.cc99
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_5469.cc172
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_789.cc177
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_935.cc132
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_abort1.cc194
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_abort2.cc147
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_abort3.cc196
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_abort4.cc263
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_abort5.cc251
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_abort_delete_first.cc174
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_archive0.cc73
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_archive1.cc93
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_archive2.cc102
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_bad_implicit_promotion.cc138
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_blobs_leaf_split.cc141
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_bulk_fetch.cc305
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cachesize.cc114
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cmp_descriptor.cc281
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_compression_methods.cc155
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_2.cc127
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_3.cc139
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_DB_NEXT_no_dup.cc177
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_db_current.cc162
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_delete2.cc109
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_flags.cc92
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_interrupt.cc152
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_nonleaf_expand.cc142
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_null.cc210
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_stickyness.cc133
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_cursor_with_read_txn.cc125
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_already_exists.cc97
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_change_pagesize.cc104
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_change_xxx.cc150
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_close_no_open.cc65
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_current_clobbers_db.cc109
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_dbt_mem_behavior.cc192
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_delete.cc186
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_descriptor.cc336
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_env_open_close.cc59
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_env_open_nocreate.cc92
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_env_open_open_close.cc75
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_env_set_errpfx.cc74
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_env_set_lg_dir.cc80
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_env_set_tmp_dir.cc80
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_env_strdup_null.cc67
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_get_put_flags.cc172
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_named_delete_last.cc133
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_no_env.cc62
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_open_notexist_reopen.cc67
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_remove.cc79
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_remove_subdb.cc125
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_set_flags.cc84
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_subdb.cc95
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_subdb_different_flags.cc114
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_txn_locks_nonheaviside.cc612
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_txn_locks_read_uncommitted.cc241
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_db_version.cc61
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_env_close_flags.cc84
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_env_create_db_create.cc60
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_env_open_flags.cc92
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_equal_keys_with_different_bytes.cc97
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_error.cc131
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_forkjoin.cc59
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_get_max_row_size.cc74
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_get_zeroed_dbt.cc80
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_groupcommit_count.cc220
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_groupcommit_perf.cc147
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_hsoc.cc151
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_insert_cursor_delete_insert.cc113
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_insert_many_gc.cc105
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_insert_memleak.cc96
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_insert_unique.cc159
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_iterate_live_transactions.cc137
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_iterate_pending_lock_requests.cc134
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_keylen_diff.cc232
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_kv_gen.h226
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_kv_limits.cc211
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_large_update_broadcast_small_cachetable.cc191
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_lock_timeout_callback.cc140
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_locking_with_read_txn.cc90
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_locktree_close.cc115
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log0.cc62
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log1.cc111
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log10.cc147
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log1_abort.cc90
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log2.cc83
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log2_abort.cc76
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log3.cc91
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log3_abort.cc93
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log4.cc99
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log4_abort.cc104
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log5.cc118
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log5_abort.cc123
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log6.cc158
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log6_abort.cc163
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log6a_abort.cc338
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log7.cc123
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log8.cc147
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_log9.cc146
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_logflush.cc97
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_logmax.cc139
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_memcmp_magic.cc169
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_mostly_seq.cc110
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_multiple_checkpoints_block_commit.cc136
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_nested.cc185
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_nodup_set.cc208
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_query.cc433
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_rand_insert.cc133
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_read_txn_invalid_ops.cc196
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_redirect_func.cc188
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_restrict.cc305
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_reverse_compare_fun.cc179
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_set_func_malloc.cc123
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_simple_read_txn.cc97
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress0.cc186
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress1.cc146
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress2.cc140
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress3.cc143
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress4.cc139
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress5.cc113
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress6.cc171
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress7.cc109
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress_hot_indexing.cc334
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress_openclose.cc56
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_stress_with_verify.cc110
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_thread_flags.cc132
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_thread_insert.cc171
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt.cc132
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt2.cc132
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt3.cc132
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt4.cc132
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_transactional_descriptor.cc227
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_abort5.cc112
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_abort5a.cc132
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_abort6.cc162
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_abort7.cc119
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_begin_commit.cc70
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_close_before_commit.cc89
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_close_before_prepare_commit.cc92
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_cursor_last.cc249
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested1.cc173
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested2.cc245
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested3.cc281
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested4.cc367
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested5.cc386
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort.cc128
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort2.cc117
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort3.cc123
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort4.cc148
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_read_committed_always.cc121
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_txn_recover3.cc140
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_unused_memory_crash.cc137
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_abort_works.cc191
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_abort_works.cc182
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_calls_back.cc136
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_can_delete_elements.cc166
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_changes_values.cc154
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_indexer.cc225
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_loader.cc178
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_nested_updates.cc165
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_previously_deleted.cc196
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_stress.cc177
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_update_fun_has_choices.cc176
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_broadcast_with_empty_table.cc107
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_calls_back.cc136
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_can_delete_elements.cc168
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_changes_values.cc162
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_nested_updates.cc174
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_nonexistent_keys.cc193
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_previously_deleted.cc202
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_stress.cc187
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_txn_snapshot_works_concurrently.cc183
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_txn_snapshot_works_correctly_with_deletes.cc168
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_update_with_empty_table.cc142
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_updates_single_key.cc101
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_weakxaction.cc101
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_zero_length_keys.cc188
-rw-r--r--storage/tokudb/PerconaFT/src/tests/threaded_stress_test_helpers.h2898
-rw-r--r--storage/tokudb/PerconaFT/src/tests/time_create_db.cc122
-rw-r--r--storage/tokudb/PerconaFT/src/tests/transactional_fileops.cc459
-rw-r--r--storage/tokudb/PerconaFT/src/tests/update-multiple-data-diagonal.cc343
-rw-r--r--storage/tokudb/PerconaFT/src/tests/update-multiple-key0.cc327
-rw-r--r--storage/tokudb/PerconaFT/src/tests/update-multiple-nochange.cc319
-rw-r--r--storage/tokudb/PerconaFT/src/tests/update-multiple-with-indexer-array.cc459
-rw-r--r--storage/tokudb/PerconaFT/src/tests/update-multiple-with-indexer.cc358
-rw-r--r--storage/tokudb/PerconaFT/src/tests/update.cc91
-rw-r--r--storage/tokudb/PerconaFT/src/tests/upgrade-test-1.cc263
-rw-r--r--storage/tokudb/PerconaFT/src/tests/upgrade-test-2.cc244
-rw-r--r--storage/tokudb/PerconaFT/src/tests/upgrade-test-3.cc260
-rw-r--r--storage/tokudb/PerconaFT/src/tests/upgrade-test-4.cc364
-rw-r--r--storage/tokudb/PerconaFT/src/tests/upgrade-test-5.cc245
-rw-r--r--storage/tokudb/PerconaFT/src/tests/upgrade-test-6.cc416
-rw-r--r--storage/tokudb/PerconaFT/src/tests/upgrade-test-7.cc144
-rw-r--r--storage/tokudb/PerconaFT/src/tests/upgrade_simple.cc160
-rw-r--r--storage/tokudb/PerconaFT/src/tests/xa-bigtxn-discard-abort.cc209
-rw-r--r--storage/tokudb/PerconaFT/src/tests/xa-bigtxn-discard-commit.cc206
-rw-r--r--storage/tokudb/PerconaFT/src/tests/xa-dirty-commit.cc141
-rw-r--r--storage/tokudb/PerconaFT/src/tests/xa-dirty-rollback.cc141
-rw-r--r--storage/tokudb/PerconaFT/src/tests/xa-txn-discard-abort.cc143
-rw-r--r--storage/tokudb/PerconaFT/src/tests/xa-txn-discard-commit.cc144
-rw-r--r--storage/tokudb/PerconaFT/src/tests/zombie_db.cc158
-rw-r--r--storage/tokudb/PerconaFT/src/toku_patent.cc66
-rw-r--r--storage/tokudb/PerconaFT/src/ydb-internal.h277
-rw-r--r--storage/tokudb/PerconaFT/src/ydb.cc3164
-rw-r--r--storage/tokudb/PerconaFT/src/ydb.h60
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_cursor.cc890
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_cursor.h61
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_db.cc1211
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_db.h121
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_env_func.cc184
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_env_func.h52
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_lib.cc57
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_load.h62
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_row_lock.cc276
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_row_lock.h61
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_txn.cc620
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_txn.h59
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_write.cc1136
-rw-r--r--storage/tokudb/PerconaFT/src/ydb_write.h104
679 files changed, 106169 insertions, 0 deletions
diff --git a/storage/tokudb/PerconaFT/src/CMakeLists.txt b/storage/tokudb/PerconaFT/src/CMakeLists.txt
new file mode 100644
index 00000000000..5e5d2d2da7a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/CMakeLists.txt
@@ -0,0 +1,56 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/..")
+
+set(tokudb_srcs
+ ydb
+ ydb_cursor
+ ydb_db
+ ydb_env_func
+ ydb_row_lock
+ ydb_txn
+ ydb_write
+ errors
+ loader
+ indexer
+ indexer-undo-do
+ toku_patent
+ )
+
+## make the shared library
+add_library(${LIBTOKUDB} SHARED ${tokudb_srcs})
+add_dependencies(${LIBTOKUDB} install_tdb_h generate_log_code)
+target_link_libraries(${LIBTOKUDB} LINK_PRIVATE locktree_static ft_static util_static lzma snappy ${LIBTOKUPORTABILITY})
+target_link_libraries(${LIBTOKUDB} LINK_PUBLIC z)
+
+## make the static library
+add_library(tokudb_static_conv STATIC ${tokudb_srcs})
+add_dependencies(tokudb_static_conv install_tdb_h generate_log_code)
+set_target_properties(tokudb_static_conv PROPERTIES POSITION_INDEPENDENT_CODE ON)
+set(tokudb_source_libs tokudb_static_conv locktree_static ft_static util_static lzma snappy)
+toku_merge_static_libs(${LIBTOKUDB}_static ${LIBTOKUDB}_static "${tokudb_source_libs}")
+
+## add gcov and define _GNU_SOURCE
+maybe_add_gcov_to_libraries(${LIBTOKUDB} tokudb_static_conv)
+set_property(TARGET ${LIBTOKUDB} tokudb_static_conv APPEND PROPERTY COMPILE_DEFINITIONS _GNU_SOURCE)
+
+## add a version script and set -fvisibility=hidden for the shared library
+configure_file(export.map . COPYONLY)
+if (NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ add_space_separated_property(TARGET ${LIBTOKUDB} COMPILE_FLAGS "-fvisibility=hidden -fvisibility-inlines-hidden")
+ add_space_separated_property(TARGET ${LIBTOKUDB} LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/export.map")
+endif ()
+
+# detect when we are being built as a subproject
+if (NOT DEFINED MYSQL_PROJECT_NAME_DOCSTRING)
+ install(
+ TARGETS ${LIBTOKUDB}
+ DESTINATION ${INSTALL_LIBDIR}
+ COMPONENT tokukv_libs_shared
+ )
+ install(
+ TARGETS ${LIBTOKUDB}_static
+ DESTINATION ${INSTALL_LIBDIR}
+ COMPONENT tokukv_libs_static
+ )
+endif ()
+
+add_subdirectory(tests)
diff --git a/storage/tokudb/PerconaFT/src/errors.cc b/storage/tokudb/PerconaFT/src/errors.cc
new file mode 100644
index 00000000000..0ec5b6d974c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/errors.cc
@@ -0,0 +1,141 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/**
+ \file errors.c
+ \brief Error handling
+
+ The error handling routines for ydb
+*/
+
+#include <toku_portability.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "ydb-internal.h"
+
+/** Checks whether the environment has panicked */
+int toku_env_is_panicked(DB_ENV *dbenv /**< The environment to check */) {
+ if (dbenv==0) return 0;
+ return dbenv->i->is_panicked;
+}
+
+/* Prints an error message to a file specified by env (or stderr),
+ preceded by the environment's error prefix. */
+static void toku__ydb_error_file(const DB_ENV *env, bool use_stderr,
+ char errmsg[]) {
+ /* Determine the error file to use */
+ FILE *CAST_FROM_VOIDP(efile, env->i->errfile);
+ if (efile==NULL && env->i->errcall==0 && use_stderr) efile = stderr;
+
+ /* Print out on a file */
+ if (efile) {
+ if (env->i->errpfx) fprintf(efile, "%s: ", env->i->errpfx);
+ fprintf(efile, "%s", errmsg);
+ }
+}
+
+/**
+
+ Prints out environment errors, adjusting to a variety of options
+ and formats.
+ The printout format can be controlled to print the following optional
+ messages:
+ - The environment error message prefix
+ - User-supplied prefix obtained by printing ap with the
+ fmt string
+ - The standard db error string
+ The print out takes place via errcall (if set), errfile (if set),
+ or stderr if neither is set (and the user so toggles the printout).
+ Both errcall and errfile can be set.
+ The error message is truncated to approximately 4,000 characters.
+
+ \param env The environment that the error refers to.
+ \param error The error code
+ \param include_stderrstring Controls whether the standard db error
+ string should be included in the print out
+ \param use_stderr_if_nothing_else Toggles the use of stderr.
+ \param fmt Output format for optional prefix arguments (must be NULL
+ if the prefix is empty)
+ \param ap Optional prefix
+*/
+void toku_ydb_error_all_cases(const DB_ENV * env,
+ int error,
+ bool include_stderrstring,
+ bool use_stderr_if_nothing_else,
+ const char *fmt, va_list ap) {
+ /* Construct the error message */
+ char buf [4000];
+ int count=0;
+ if (fmt) count=vsnprintf(buf, sizeof(buf), fmt, ap);
+ if (include_stderrstring) {
+ count+=snprintf(&buf[count], sizeof(buf)-count, ": %s",
+ db_strerror(error));
+ }
+
+ /* Print via errcall */
+ if (env->i->errcall) env->i->errcall(env, env->i->errpfx, buf);
+
+ /* Print out on a file */
+ toku__ydb_error_file(env, use_stderr_if_nothing_else, buf);
+}
+
+/** Handle all the error cases (but don't do the default thing.)
+ \param dbenv The environment that is subject to errors
+ \param error The error code
+ \param fmt The format string for additional variable arguments to
+ be printed */
+int toku_ydb_do_error (const DB_ENV *dbenv, int error, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ toku_ydb_error_all_cases(dbenv, error, false, false, fmt, ap);
+ va_end(ap);
+ return error;
+}
+
+/** Handle errors on an environment,
+ \param dbenv The environment that is subject to errors
+ \param error The error code
+ \param fmt The format string for additional variable arguments to
+ be printed */
+void toku_env_err(const DB_ENV * env, int error, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ toku_ydb_error_all_cases(env, error, false, true, fmt, ap);
+ va_end(ap);
+}
diff --git a/storage/tokudb/PerconaFT/src/export.map b/storage/tokudb/PerconaFT/src/export.map
new file mode 100644
index 00000000000..3f2c7569ea4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/export.map
@@ -0,0 +1,98 @@
+{
+ global:
+ db_create;
+ db_env_create;
+ db_strerror;
+ db_version;
+ db_env_set_direct_io;
+ db_env_set_compress_buffers_before_eviction;
+ db_env_set_func_fsync;
+ db_env_set_func_malloc;
+ db_env_set_func_realloc;
+ db_env_set_func_free;
+ db_env_set_func_pwrite;
+ db_env_set_func_full_pwrite;
+ db_env_set_func_write;
+ db_env_set_func_full_write;
+ db_env_set_func_fdopen;
+ db_env_set_func_fopen;
+ db_env_set_func_open;
+ db_env_set_func_fclose;
+ db_env_set_func_pread;
+ db_env_set_func_loader_fwrite;
+ db_env_set_checkpoint_callback;
+ db_env_set_checkpoint_callback2;
+ db_env_set_recover_callback;
+ db_env_set_recover_callback2;
+ db_env_set_loader_size_factor;
+ db_env_set_mvcc_garbage_collection_verification;
+ db_env_enable_engine_status;
+ db_env_set_flusher_thread_callback;
+ db_env_set_num_bucket_mutexes;
+ db_env_set_toku_product_name;
+ db_env_try_gdb_stack_trace;
+
+ read_partitioned_counter;
+
+ toku_ydb_error_all_cases;
+ toku_set_trace_file;
+ toku_close_trace_file;
+
+ toku_add_trace_mem;
+ toku_print_trace_mem;
+
+
+ toku_free;
+ toku_malloc;
+ toku_calloc;
+ toku_xmemdup;
+ toku_xrealloc;
+ toku_os_get_file_size;
+ toku_os_getpid;
+ toku_os_gettid;
+ toku_os_initialize_settings;
+ toku_os_is_absolute_name;
+ toku_os_mkdir;
+ toku_realloc;
+ toku_strdup;
+ toku_pthread_yield;
+
+ toku_cachetable_print_hash_histogram;
+ toku_set_lsn_increment;
+
+ toku_builtin_compare_fun;
+
+ toku_stat;
+ toku_fstat;
+ toku_dup2;
+
+ toku_os_full_write;
+ toku_os_full_pwrite;
+
+ toku_os_get_max_process_data_size;
+ toku_os_get_phys_memory_size;
+
+ tokutime_to_seconds;
+
+ toku_do_assert;
+ toku_do_assert_fail;
+ toku_do_assert_zero_fail;
+ toku_set_assert_on_write_enospc;
+
+ toku_test_db_redirect_dictionary;
+ toku_test_get_latest_lsn;
+ toku_test_get_checkpointing_user_data_status;
+ toku_indexer_set_test_only_flags;
+ toku_increase_last_xid;
+
+ toku_patent_string;
+ toku_copyright_string;
+
+ toku_dbt_array_init;
+ toku_dbt_array_destroy;
+ toku_dbt_array_destroy_shallow;
+ toku_dbt_array_resize;
+
+ local: *;
+};
+
diff --git a/storage/tokudb/PerconaFT/src/indexer-internal.h b/storage/tokudb/PerconaFT/src/indexer-internal.h
new file mode 100644
index 00000000000..48e62ee49b2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/indexer-internal.h
@@ -0,0 +1,116 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+#include <ft/txn/txn_state.h>
+#include <toku_pthread.h>
+
+// the indexer_commit_keys is an ordered set of keys described by a DBT in the keys array.
+// the array is a resizeable array with max size "max_keys" and current size "current_keys".
+// the ordered set is used by the hotindex undo function to collect the commit keys.
+struct indexer_commit_keys {
+ int max_keys; // max number of keys
+ int current_keys; // number of valid keys
+ DBT *keys; // the variable length keys array
+};
+
+// a ule and all of its provisional txn info
+// used by the undo-do algorithm to gather up ule provisional info in
+// a cursor callback that provides exclusive access to the source DB
+// with respect to txn commit and abort
+struct ule_prov_info {
+ // these are pointers to the allocated leafentry and ule needed to calculate
+ // provisional info. we only borrow them - whoever created the provisional info
+ // is responsible for cleaning up the leafentry and ule when done.
+ LEAFENTRY le;
+ ULEHANDLE ule;
+ void* key;
+ uint32_t keylen;
+ // provisional txn info for the ule
+ uint32_t num_provisional;
+ uint32_t num_committed;
+ TXNID *prov_ids;
+ TOKUTXN *prov_txns;
+ TOKUTXN_STATE *prov_states;
+};
+
+struct __toku_indexer_internal {
+ DB_ENV *env;
+ DB_TXN *txn;
+ toku_mutex_t indexer_lock;
+ toku_mutex_t indexer_estimate_lock;
+ DBT position_estimate;
+ DB *src_db;
+ int N;
+ DB **dest_dbs; /* [N] */
+ uint32_t indexer_flags;
+ void (*error_callback)(DB *db, int i, int err, DBT *key, DBT *val, void *error_extra);
+ void *error_extra;
+ int (*poll_func)(void *poll_extra, float progress);
+ void *poll_extra;
+ uint64_t estimated_rows; // current estimate of table size
+ uint64_t loop_mod; // how often to call poll_func
+ LE_CURSOR lec;
+ FILENUM *fnums; /* [N] */
+ FILENUMS filenums;
+
+ // undo state
+ struct indexer_commit_keys commit_keys; // set of keys to commit
+ DBT_ARRAY *hot_keys;
+ DBT_ARRAY *hot_vals;
+
+ // test functions
+ int (*undo_do)(DB_INDEXER *indexer, DB *hotdb, DBT* key, ULEHANDLE ule);
+ TOKUTXN_STATE (*test_xid_state)(DB_INDEXER *indexer, TXNID xid);
+ void (*test_lock_key)(DB_INDEXER *indexer, TXNID xid, DB *hotdb, DBT *key);
+ int (*test_delete_provisional)(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids);
+ int (*test_delete_committed)(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids);
+ int (*test_insert_provisional)(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, DBT *hotval, XIDS xids);
+ int (*test_insert_committed)(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, DBT *hotval, XIDS xids);
+ int (*test_commit_any)(DB_INDEXER *indexer, DB *db, DBT *key, XIDS xids);
+
+ // test flags
+ int test_only_flags;
+};
+
+void indexer_undo_do_init(DB_INDEXER *indexer);
+
+void indexer_undo_do_destroy(DB_INDEXER *indexer);
+
+int indexer_undo_do(DB_INDEXER *indexer, DB *hotdb, struct ule_prov_info *prov_info, DBT_ARRAY *hot_keys, DBT_ARRAY *hot_vals);
diff --git a/storage/tokudb/PerconaFT/src/indexer-undo-do.cc b/storage/tokudb/PerconaFT/src/indexer-undo-do.cc
new file mode 100644
index 00000000000..b93429407eb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/indexer-undo-do.cc
@@ -0,0 +1,652 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <toku_portability.h>
+#include <toku_assert.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <ft/le-cursor.h>
+#include <ft/ft-ops.h>
+#include <ft/leafentry.h>
+#include <ft/ule.h>
+#include <ft/txn/txn_manager.h>
+#include <ft/txn/xids.h>
+#include <ft/cachetable/checkpoint.h>
+
+#include "ydb-internal.h"
+#include "ydb_row_lock.h"
+#include "indexer.h"
+#include "indexer-internal.h"
+
+// initialize the commit keys
+static void
+indexer_commit_keys_init(struct indexer_commit_keys *keys) {
+ keys->max_keys = keys->current_keys = 0;
+ keys->keys = NULL;
+}
+
+// destroy the commit keys
+static void
+indexer_commit_keys_destroy(struct indexer_commit_keys *keys) {
+ for (int i = 0; i < keys->max_keys; i++)
+ toku_destroy_dbt(&keys->keys[i]);
+ toku_free(keys->keys);
+}
+
+// return the number of keys in the ordered set
+static int
+indexer_commit_keys_valid(struct indexer_commit_keys *keys) {
+ return keys->current_keys;
+}
+
+// add a key to the commit keys
+static void
+indexer_commit_keys_add(struct indexer_commit_keys *keys, size_t length, void *ptr) {
+ if (keys->current_keys >= keys->max_keys) {
+ int new_max_keys = keys->max_keys == 0 ? 256 : keys->max_keys * 2;
+ keys->keys = (DBT *) toku_xrealloc(keys->keys, new_max_keys * sizeof (DBT));
+ for (int i = keys->current_keys; i < new_max_keys; i++)
+ toku_init_dbt_flags(&keys->keys[i], DB_DBT_REALLOC);
+ keys->max_keys = new_max_keys;
+ }
+ DBT *key = &keys->keys[keys->current_keys];
+ toku_dbt_set(length, ptr, key, NULL);
+ keys->current_keys++;
+}
+
+// set the ordered set to empty
+static void
+indexer_commit_keys_set_empty(struct indexer_commit_keys *keys) {
+ keys->current_keys = 0;
+}
+
+// internal functions
+static int indexer_set_xid(DB_INDEXER *indexer, TXNID xid, XIDS *xids_result);
+static int indexer_append_xid(DB_INDEXER *indexer, TXNID xid, XIDS *xids_result);
+
+static bool indexer_find_prev_xr(DB_INDEXER *indexer, ULEHANDLE ule, uint64_t xrindex, uint64_t *prev_xrindex);
+
+static int indexer_generate_hot_keys_vals(DB_INDEXER *indexer, DB *hotdb, struct ule_prov_info* prov_info, UXRHANDLE uxr, DBT_ARRAY *hotkeys, DBT_ARRAY *hotvals);
+static int indexer_ft_delete_provisional(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids, TOKUTXN txn);
+static int indexer_ft_delete_committed(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids);
+static int indexer_ft_insert_provisional(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, DBT *hotval, XIDS xids, TOKUTXN txn);
+static int indexer_ft_insert_committed(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, DBT *hotval, XIDS xids);
+static int indexer_ft_commit(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids);
+static void indexer_lock_key(DB_INDEXER *indexer, DB *hotdb, DBT *key, TXNID outermost_live_xid, TOKUTXN txn);
+
+
+// initialize undo globals located in the indexer private object
+void
+indexer_undo_do_init(DB_INDEXER *indexer) {
+ indexer_commit_keys_init(&indexer->i->commit_keys);
+ XMALLOC_N(indexer->i->N, indexer->i->hot_keys);
+ XMALLOC_N(indexer->i->N, indexer->i->hot_vals);
+ for (int which = 0; which < indexer->i->N; which++) {
+ toku_dbt_array_init(&indexer->i->hot_keys[which], 1);
+ toku_dbt_array_init(&indexer->i->hot_vals[which], 1);
+ }
+}
+
+// destroy the undo globals
+void
+indexer_undo_do_destroy(DB_INDEXER *indexer) {
+ indexer_commit_keys_destroy(&indexer->i->commit_keys);
+ if (indexer->i->hot_keys) {
+ invariant(indexer->i->hot_vals);
+ for (int which = 0; which < indexer->i->N; which++) {
+ toku_dbt_array_destroy(&indexer->i->hot_keys[which]);
+ toku_dbt_array_destroy(&indexer->i->hot_vals[which]);
+ }
+ toku_free(indexer->i->hot_keys);
+ toku_free(indexer->i->hot_vals);
+ }
+}
+
+static int
+indexer_undo_do_committed(DB_INDEXER *indexer, DB *hotdb, struct ule_prov_info *prov_info, DBT_ARRAY *hot_keys, DBT_ARRAY *hot_vals) {
+ int result = 0;
+ ULEHANDLE ule = prov_info->ule;
+
+ // init the xids to the root xid
+ XIDS xids = toku_xids_get_root_xids();
+
+ // scan the committed stack from bottom to top
+ uint32_t num_committed = ule_get_num_committed(ule);
+ for (uint64_t xrindex = 0; xrindex < num_committed; xrindex++) {
+
+ indexer_commit_keys_set_empty(&indexer->i->commit_keys);
+
+ // get the transaction record
+ UXRHANDLE uxr = ule_get_uxr(ule, xrindex);
+
+ // setup up the xids
+ TXNID this_xid = uxr_get_txnid(uxr);
+ result = indexer_set_xid(indexer, this_xid, &xids);
+ if (result != 0)
+ break;
+
+ // placeholders in the committed stack are not allowed
+ invariant(!uxr_is_placeholder(uxr));
+
+ // undo
+ if (xrindex > 0) {
+ uint64_t prev_xrindex = xrindex - 1;
+ UXRHANDLE prevuxr = ule_get_uxr(ule, prev_xrindex);
+ if (uxr_is_delete(prevuxr)) {
+ ; // do nothing
+ } else if (uxr_is_insert(prevuxr)) {
+ // generate the hot delete key
+ result = indexer_generate_hot_keys_vals(indexer, hotdb, prov_info, prevuxr, hot_keys, NULL);
+ if (result == 0) {
+ paranoid_invariant(hot_keys->size <= hot_keys->capacity);
+ for (uint32_t i = 0; i < hot_keys->size; i++) {
+ DBT *hotkey = &hot_keys->dbts[i];
+
+ // send the delete message
+ result = indexer_ft_delete_committed(indexer, hotdb, hotkey, xids);
+ if (result == 0) {
+ indexer_commit_keys_add(&indexer->i->commit_keys, hotkey->size, hotkey->data);
+ }
+ }
+ }
+ } else {
+ assert(0);
+ }
+ }
+ if (result != 0) {
+ break;
+ }
+
+ // do
+ if (uxr_is_delete(uxr)) {
+ ; // do nothing
+ } else if (uxr_is_insert(uxr)) {
+ // generate the hot insert key and val
+ result = indexer_generate_hot_keys_vals(indexer, hotdb, prov_info, uxr, hot_keys, hot_vals);
+ if (result == 0) {
+ paranoid_invariant(hot_keys->size == hot_vals->size);
+ paranoid_invariant(hot_keys->size <= hot_keys->capacity);
+ paranoid_invariant(hot_vals->size <= hot_vals->capacity);
+ for (uint32_t i = 0; i < hot_keys->size; i++) {
+ DBT *hotkey = &hot_keys->dbts[i];
+ DBT *hotval = &hot_vals->dbts[i];
+
+ // send the insert message
+ result = indexer_ft_insert_committed(indexer, hotdb, hotkey, hotval, xids);
+ if (result == 0) {
+ indexer_commit_keys_add(&indexer->i->commit_keys, hotkey->size, hotkey->data);
+ }
+ }
+ }
+ } else
+ assert(0);
+
+ // send commit messages if needed
+ for (int i = 0; result == 0 && i < indexer_commit_keys_valid(&indexer->i->commit_keys); i++)
+ result = indexer_ft_commit(indexer, hotdb, &indexer->i->commit_keys.keys[i], xids);
+
+ if (result != 0)
+ break;
+ }
+
+ toku_xids_destroy(&xids);
+
+ return result;
+}
+
+static void release_txns(
+ ULEHANDLE ule,
+ TOKUTXN_STATE* prov_states,
+ TOKUTXN* prov_txns,
+ DB_INDEXER *indexer
+ )
+{
+ uint32_t num_provisional = ule_get_num_provisional(ule);
+ if (indexer->i->test_xid_state) {
+ goto exit;
+ }
+ for (uint32_t i = 0; i < num_provisional; i++) {
+ if (prov_states[i] == TOKUTXN_LIVE || prov_states[i] == TOKUTXN_PREPARING) {
+ toku_txn_unpin_live_txn(prov_txns[i]);
+ }
+ }
+exit:
+ return;
+}
+
+static int
+indexer_undo_do_provisional(DB_INDEXER *indexer, DB *hotdb, struct ule_prov_info *prov_info, DBT_ARRAY *hot_keys, DBT_ARRAY *hot_vals) {
+ int result = 0;
+ indexer_commit_keys_set_empty(&indexer->i->commit_keys);
+ ULEHANDLE ule = prov_info->ule;
+
+ // init the xids to the root xid
+ XIDS xids = toku_xids_get_root_xids();
+
+ uint32_t num_provisional = prov_info->num_provisional;
+ uint32_t num_committed = prov_info->num_committed;
+ TXNID *prov_ids = prov_info->prov_ids;
+ TOKUTXN *prov_txns = prov_info->prov_txns;
+ TOKUTXN_STATE *prov_states = prov_info->prov_states;
+
+ // nothing to do if there's nothing provisional
+ if (num_provisional == 0) {
+ goto exit;
+ }
+
+ TXNID outermost_xid_state;
+ outermost_xid_state = prov_states[0];
+
+ // scan the provisional stack from the outermost to the innermost transaction record
+ TOKUTXN curr_txn;
+ curr_txn = NULL;
+ for (uint64_t xrindex = num_committed; xrindex < num_committed + num_provisional; xrindex++) {
+
+ // get the ith transaction record
+ UXRHANDLE uxr = ule_get_uxr(ule, xrindex);
+
+ TXNID this_xid = uxr_get_txnid(uxr);
+ TOKUTXN_STATE this_xid_state = prov_states[xrindex - num_committed];
+
+ if (this_xid_state == TOKUTXN_ABORTING) {
+ break; // nothing to do once we reach a transaction that is aborting
+ }
+
+ if (xrindex == num_committed) { // if this is the outermost xr
+ result = indexer_set_xid(indexer, this_xid, &xids); // always add the outermost xid to the XIDS list
+ curr_txn = prov_txns[xrindex - num_committed];
+ } else {
+ switch (this_xid_state) {
+ case TOKUTXN_LIVE:
+ result = indexer_append_xid(indexer, this_xid, &xids); // append a live xid to the XIDS list
+ curr_txn = prov_txns[xrindex - num_committed];
+ if (!indexer->i->test_xid_state) {
+ assert(curr_txn);
+ }
+ break;
+ case TOKUTXN_PREPARING:
+ assert(0); // not allowed
+ case TOKUTXN_COMMITTING:
+ case TOKUTXN_ABORTING:
+ case TOKUTXN_RETIRED:
+ break; // nothing to do
+ }
+ }
+ if (result != 0)
+ break;
+
+ if (outermost_xid_state != TOKUTXN_LIVE && xrindex > num_committed) {
+ // if the outermost is not live, then the inner state must be retired. thats the way that the txn API works.
+ assert(this_xid_state == TOKUTXN_RETIRED);
+ }
+
+ if (uxr_is_placeholder(uxr)) {
+ continue; // skip placeholders
+ }
+ // undo
+ uint64_t prev_xrindex;
+ bool prev_xrindex_found = indexer_find_prev_xr(indexer, ule, xrindex, &prev_xrindex);
+ if (prev_xrindex_found) {
+ UXRHANDLE prevuxr = ule_get_uxr(ule, prev_xrindex);
+ if (uxr_is_delete(prevuxr)) {
+ ; // do nothing
+ } else if (uxr_is_insert(prevuxr)) {
+ // generate the hot delete key
+ result = indexer_generate_hot_keys_vals(indexer, hotdb, prov_info, prevuxr, hot_keys, NULL);
+ if (result == 0) {
+ paranoid_invariant(hot_keys->size <= hot_keys->capacity);
+ for (uint32_t i = 0; i < hot_keys->size; i++) {
+ DBT *hotkey = &hot_keys->dbts[i];
+
+ // send the delete message
+ switch (outermost_xid_state) {
+ case TOKUTXN_LIVE:
+ case TOKUTXN_PREPARING:
+ invariant(this_xid_state != TOKUTXN_ABORTING);
+ invariant(!curr_txn || toku_txn_get_state(curr_txn) == TOKUTXN_LIVE || toku_txn_get_state(curr_txn) == TOKUTXN_PREPARING);
+ result = indexer_ft_delete_provisional(indexer, hotdb, hotkey, xids, curr_txn);
+ if (result == 0) {
+ indexer_lock_key(indexer, hotdb, hotkey, prov_ids[0], curr_txn);
+ }
+ break;
+ case TOKUTXN_COMMITTING:
+ case TOKUTXN_RETIRED:
+ result = indexer_ft_delete_committed(indexer, hotdb, hotkey, xids);
+ if (result == 0)
+ indexer_commit_keys_add(&indexer->i->commit_keys, hotkey->size, hotkey->data);
+ break;
+ case TOKUTXN_ABORTING: // can not happen since we stop processing the leaf entry if the outer most xr is aborting
+ assert(0);
+ }
+ }
+ }
+ } else
+ assert(0);
+ }
+ if (result != 0)
+ break;
+
+ // do
+ if (uxr_is_delete(uxr)) {
+ ; // do nothing
+ } else if (uxr_is_insert(uxr)) {
+ // generate the hot insert key and val
+ result = indexer_generate_hot_keys_vals(indexer, hotdb, prov_info, uxr, hot_keys, hot_vals);
+ if (result == 0) {
+ paranoid_invariant(hot_keys->size == hot_vals->size);
+ paranoid_invariant(hot_keys->size <= hot_keys->capacity);
+ paranoid_invariant(hot_vals->size <= hot_vals->capacity);
+ for (uint32_t i = 0; i < hot_keys->size; i++) {
+ DBT *hotkey = &hot_keys->dbts[i];
+ DBT *hotval = &hot_vals->dbts[i];
+
+ // send the insert message
+ switch (outermost_xid_state) {
+ case TOKUTXN_LIVE:
+ case TOKUTXN_PREPARING:
+ assert(this_xid_state != TOKUTXN_ABORTING);
+ invariant(!curr_txn || toku_txn_get_state(curr_txn) == TOKUTXN_LIVE || toku_txn_get_state(curr_txn) == TOKUTXN_PREPARING);
+ result = indexer_ft_insert_provisional(indexer, hotdb, hotkey, hotval, xids, curr_txn);
+ if (result == 0) {
+ indexer_lock_key(indexer, hotdb, hotkey, prov_ids[0], prov_txns[0]);
+ }
+ break;
+ case TOKUTXN_COMMITTING:
+ case TOKUTXN_RETIRED:
+ result = indexer_ft_insert_committed(indexer, hotdb, hotkey, hotval, xids);
+ // no need to do this because we do implicit commits on inserts
+ if (0 && result == 0)
+ indexer_commit_keys_add(&indexer->i->commit_keys, hotkey->size, hotkey->data);
+ break;
+ case TOKUTXN_ABORTING: // can not happen since we stop processing the leaf entry if the outer most xr is aborting
+ assert(0);
+ }
+ }
+ }
+ } else
+ assert(0);
+
+ if (result != 0)
+ break;
+ }
+
+ // send commits if the outermost provisional transaction is committed
+ for (int i = 0; result == 0 && i < indexer_commit_keys_valid(&indexer->i->commit_keys); i++) {
+ result = indexer_ft_commit(indexer, hotdb, &indexer->i->commit_keys.keys[i], xids);
+ }
+
+ // be careful with this in the future. Right now, only exit path
+ // is BEFORE we call fill_prov_info, so this happens before exit
+ // If in the future we add a way to exit after fill_prov_info,
+ // then this will need to be handled below exit
+ release_txns(ule, prov_states, prov_txns, indexer);
+exit:
+ toku_xids_destroy(&xids);
+ return result;
+}
+
+int
+indexer_undo_do(DB_INDEXER *indexer, DB *hotdb, struct ule_prov_info *prov_info, DBT_ARRAY *hot_keys, DBT_ARRAY *hot_vals) {
+ int result = indexer_undo_do_committed(indexer, hotdb, prov_info, hot_keys, hot_vals);
+ if (result == 0) {
+ result = indexer_undo_do_provisional(indexer, hotdb, prov_info, hot_keys, hot_vals);
+ }
+ if (indexer->i->test_only_flags == INDEXER_TEST_ONLY_ERROR_CALLBACK) {
+ result = EINVAL;
+ }
+
+ return result;
+}
+
+// set xids_result = [root_xid, this_xid]
+// Note that this could be sped up by adding a new xids constructor that constructs the stack with
+// exactly one xid.
+static int
+indexer_set_xid(DB_INDEXER *UU(indexer), TXNID this_xid, XIDS *xids_result) {
+ int result = 0;
+ XIDS old_xids = *xids_result;
+ XIDS new_xids = toku_xids_get_root_xids();
+ if (this_xid != TXNID_NONE) {
+ XIDS child_xids;
+ result = toku_xids_create_child(new_xids, &child_xids, this_xid);
+ toku_xids_destroy(&new_xids);
+ if (result == 0)
+ new_xids = child_xids;
+ }
+ if (result == 0) {
+ toku_xids_destroy(&old_xids);
+ *xids_result = new_xids;
+ }
+
+ return result;
+}
+
+// append xid to xids_result
+static int
+indexer_append_xid(DB_INDEXER *UU(indexer), TXNID xid, XIDS *xids_result) {
+ XIDS old_xids = *xids_result;
+ XIDS new_xids;
+ int result = toku_xids_create_child(old_xids, &new_xids, xid);
+ if (result == 0) {
+ toku_xids_destroy(&old_xids);
+ *xids_result = new_xids;
+ }
+ return result;
+}
+
+static int
+indexer_generate_hot_keys_vals(DB_INDEXER *indexer, DB *hotdb, struct ule_prov_info *prov_info, UXRHANDLE uxr, DBT_ARRAY *hotkeys, DBT_ARRAY *hotvals) {
+ int result = 0;
+
+ // setup the source key
+ DBT srckey;
+ toku_fill_dbt(&srckey, prov_info->key, prov_info->keylen);
+
+ // setup the source val
+ DBT srcval;
+ toku_fill_dbt(&srcval, uxr_get_val(uxr), uxr_get_vallen(uxr));
+
+ // generate the secondary row
+ DB_ENV *env = indexer->i->env;
+ if (hotvals) {
+ result = env->i->generate_row_for_put(hotdb, indexer->i->src_db, hotkeys, hotvals, &srckey, &srcval);
+ }
+ else {
+ result = env->i->generate_row_for_del(hotdb, indexer->i->src_db, hotkeys, &srckey, &srcval);
+ }
+ toku_destroy_dbt(&srckey);
+ toku_destroy_dbt(&srcval);
+
+ return result;
+}
+
+// Take a write lock on the given key for the outermost xid in the xids list.
+static void
+indexer_lock_key(DB_INDEXER *indexer, DB *hotdb, DBT *key, TXNID outermost_live_xid, TOKUTXN txn) {
+ // TEST
+ if (indexer->i->test_lock_key) {
+ indexer->i->test_lock_key(indexer, outermost_live_xid, hotdb, key);
+ } else {
+ toku_db_grab_write_lock(hotdb, key, txn);
+ }
+}
+
+// find the index of a non-placeholder transaction record that is previous to the transaction record
+// found at xrindex. return true if one is found and return its index in prev_xrindex. otherwise,
+// return false.
+static bool
+indexer_find_prev_xr(DB_INDEXER *UU(indexer), ULEHANDLE ule, uint64_t xrindex, uint64_t *prev_xrindex) {
+ assert(xrindex < ule_num_uxrs(ule));
+ bool prev_found = false;
+ while (xrindex > 0) {
+ xrindex -= 1;
+ UXRHANDLE uxr = ule_get_uxr(ule, xrindex);
+ if (!uxr_is_placeholder(uxr)) {
+ *prev_xrindex = xrindex;
+ prev_found = true;
+ break;
+ }
+ }
+ return prev_found;
+}
+
+// inject "delete" message into ft with logging in recovery and rollback logs,
+// and making assocation between txn and ft
+static int
+indexer_ft_delete_provisional(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids, TOKUTXN txn) {
+ int result = 0;
+ // TEST
+ if (indexer->i->test_delete_provisional) {
+ result = indexer->i->test_delete_provisional(indexer, hotdb, hotkey, xids);
+ } else {
+ result = toku_ydb_check_avail_fs_space(indexer->i->env);
+ if (result == 0) {
+ assert(txn != NULL);
+ // Not sure if this is really necessary, as
+ // the hot index DB should have to be checkpointed
+ // upon commit of the hot index transaction, but
+ // it is safe to do this
+ // this question apples to delete_committed, insert_provisional
+ // and insert_committed
+ toku_ft_maybe_delete (hotdb->i->ft_handle, hotkey, txn, false, ZERO_LSN, true);
+ }
+ }
+ return result;
+}
+
+// send a delete message into the tree without rollback or recovery logging
+static int
+indexer_ft_delete_committed(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids) {
+ int result = 0;
+ // TEST
+ if (indexer->i->test_delete_committed) {
+ result = indexer->i->test_delete_committed(indexer, hotdb, hotkey, xids);
+ } else {
+ result = toku_ydb_check_avail_fs_space(indexer->i->env);
+ if (result == 0) {
+ FT_HANDLE ft_h = db_struct_i(hotdb)->ft_handle;
+ TXN_MANAGER txn_manager = toku_ft_get_txn_manager(ft_h);
+ txn_manager_state txn_state_for_gc(txn_manager);
+
+ TXNID oldest_referenced_xid_estimate = toku_ft_get_oldest_referenced_xid_estimate(ft_h);
+ txn_gc_info gc_info(&txn_state_for_gc,
+ oldest_referenced_xid_estimate,
+ oldest_referenced_xid_estimate,
+ true);
+ toku_ft_send_delete(db_struct_i(hotdb)->ft_handle, hotkey, xids, &gc_info);
+ }
+ }
+ return result;
+}
+
+// inject "insert" message into ft with logging in recovery and rollback logs,
+// and making assocation between txn and ft
+static int
+indexer_ft_insert_provisional(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, DBT *hotval, XIDS xids, TOKUTXN txn) {
+ int result = 0;
+ // TEST
+ if (indexer->i->test_insert_provisional) {
+ result = indexer->i->test_insert_provisional(indexer, hotdb, hotkey, hotval, xids);
+ } else {
+ result = toku_ydb_check_avail_fs_space(indexer->i->env);
+ if (result == 0) {
+ assert(txn != NULL);
+ // comment/question in indexer_ft_delete_provisional applies
+ toku_ft_maybe_insert (hotdb->i->ft_handle, hotkey, hotval, txn, false, ZERO_LSN, true, FT_INSERT);
+ }
+ }
+ return result;
+}
+
+// send an insert message into the tree without rollback or recovery logging
+// and without associating the txn and the ft
+static int
+indexer_ft_insert_committed(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, DBT *hotval, XIDS xids) {
+ int result = 0;
+ // TEST
+ if (indexer->i->test_insert_committed) {
+ result = indexer->i->test_insert_committed(indexer, hotdb, hotkey, hotval, xids);
+ } else {
+ result = toku_ydb_check_avail_fs_space(indexer->i->env);
+ if (result == 0) {
+ FT_HANDLE ft_h = db_struct_i(hotdb)->ft_handle;
+ TXN_MANAGER txn_manager = toku_ft_get_txn_manager(ft_h);
+ txn_manager_state txn_state_for_gc(txn_manager);
+
+ TXNID oldest_referenced_xid_estimate = toku_ft_get_oldest_referenced_xid_estimate(ft_h);
+ txn_gc_info gc_info(&txn_state_for_gc,
+ oldest_referenced_xid_estimate,
+ oldest_referenced_xid_estimate,
+ true);
+ toku_ft_send_insert(db_struct_i(hotdb)->ft_handle, hotkey, hotval, xids, FT_INSERT, &gc_info);
+ }
+ }
+ return result;
+}
+
+// send a commit message into the tree
+// Note: If the xid is zero, then the leafentry will already have a committed transaction
+// record and no commit message is needed. (A commit message with xid of zero is
+// illegal anyway.)
+static int
+indexer_ft_commit(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids) {
+ int result = 0;
+ if (toku_xids_get_num_xids(xids) > 0) {// send commit only when not the root xid
+ // TEST
+ if (indexer->i->test_commit_any) {
+ result = indexer->i->test_commit_any(indexer, hotdb, hotkey, xids);
+ } else {
+ result = toku_ydb_check_avail_fs_space(indexer->i->env);
+ if (result == 0) {
+ FT_HANDLE ft_h = db_struct_i(hotdb)->ft_handle;
+ TXN_MANAGER txn_manager = toku_ft_get_txn_manager(ft_h);
+ txn_manager_state txn_state_for_gc(txn_manager);
+
+ TXNID oldest_referenced_xid_estimate = toku_ft_get_oldest_referenced_xid_estimate(ft_h);
+ txn_gc_info gc_info(&txn_state_for_gc,
+ oldest_referenced_xid_estimate,
+ oldest_referenced_xid_estimate,
+ true);
+ toku_ft_send_commit_any(db_struct_i(hotdb)->ft_handle, hotkey, xids, &gc_info);
+ }
+ }
+ }
+ return result;
+}
diff --git a/storage/tokudb/PerconaFT/src/indexer.cc b/storage/tokudb/PerconaFT/src/indexer.cc
new file mode 100644
index 00000000000..b475b08beb5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/indexer.cc
@@ -0,0 +1,715 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/*
+ * The indexer
+ */
+#include <stdio.h>
+#include <string.h>
+#include <toku_portability.h>
+#include "toku_assert.h"
+#include "ydb-internal.h"
+#include <ft/le-cursor.h>
+#include "indexer.h"
+#include <ft/ft-ops.h>
+#include <ft/leafentry.h>
+#include <ft/ule.h>
+#include <ft/txn/xids.h>
+#include <ft/logger/log-internal.h>
+#include <ft/cachetable/checkpoint.h>
+#include <portability/toku_atomic.h>
+#include "loader.h"
+#include <util/status.h>
+
+///////////////////////////////////////////////////////////////////////////////////
+// Engine status
+//
+// Status is intended for display to humans to help understand system behavior.
+// It does not need to be perfectly thread-safe.
+
+static INDEXER_STATUS_S indexer_status;
+
+#define STATUS_INIT(k,c,t,l,inc) TOKUFT_STATUS_INIT(indexer_status, k, c, t, "indexer: " l, inc)
+
+static void
+status_init(void) {
+ // Note, this function initializes the keyname, type, and legend fields.
+ // Value fields are initialized to zero by compiler.
+ STATUS_INIT(INDEXER_CREATE, nullptr, UINT64, "number of indexers successfully created", TOKU_ENGINE_STATUS);
+ STATUS_INIT(INDEXER_CREATE_FAIL, nullptr, UINT64, "number of calls to toku_indexer_create_indexer() that failed", TOKU_ENGINE_STATUS);
+ STATUS_INIT(INDEXER_BUILD, nullptr, UINT64, "number of calls to indexer->build() succeeded", TOKU_ENGINE_STATUS);
+ STATUS_INIT(INDEXER_BUILD_FAIL, nullptr, UINT64, "number of calls to indexer->build() failed", TOKU_ENGINE_STATUS);
+ STATUS_INIT(INDEXER_CLOSE, nullptr, UINT64, "number of calls to indexer->close() that succeeded", TOKU_ENGINE_STATUS);
+ STATUS_INIT(INDEXER_CLOSE_FAIL, nullptr, UINT64, "number of calls to indexer->close() that failed", TOKU_ENGINE_STATUS);
+ STATUS_INIT(INDEXER_ABORT, nullptr, UINT64, "number of calls to indexer->abort()", TOKU_ENGINE_STATUS);
+ STATUS_INIT(INDEXER_CURRENT, nullptr, UINT64, "number of indexers currently in existence", TOKU_ENGINE_STATUS);
+ STATUS_INIT(INDEXER_MAX, nullptr, UINT64, "max number of indexers that ever existed simultaneously", TOKU_ENGINE_STATUS);
+ indexer_status.initialized = true;
+}
+#undef STATUS_INIT
+
+void
+toku_indexer_get_status(INDEXER_STATUS statp) {
+ if (!indexer_status.initialized)
+ status_init();
+ *statp = indexer_status;
+}
+
+#define STATUS_VALUE(x) indexer_status.status[x].value.num
+
+#include "indexer-internal.h"
+
+static int build_index(DB_INDEXER *indexer);
+static int close_indexer(DB_INDEXER *indexer);
+static int abort_indexer(DB_INDEXER *indexer);
+static void free_indexer_resources(DB_INDEXER *indexer);
+static void free_indexer(DB_INDEXER *indexer);
+static int update_estimated_rows(DB_INDEXER *indexer);
+static int maybe_call_poll_func(DB_INDEXER *indexer, uint64_t loop_count);
+
+static int
+associate_indexer_with_hot_dbs(DB_INDEXER *indexer, DB *dest_dbs[], int N) {
+ int result =0;
+ for (int i = 0; i < N; i++) {
+ result = toku_db_set_indexer(dest_dbs[i], indexer);
+ if (result != 0) {
+ for (int j = 0; j < i; j++) {
+ int result2 = toku_db_set_indexer(dest_dbs[j], NULL);
+ lazy_assert(result2 == 0);
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+static void
+disassociate_indexer_from_hot_dbs(DB_INDEXER *indexer) {
+ for (int i = 0; i < indexer->i->N; i++) {
+ int result = toku_db_set_indexer(indexer->i->dest_dbs[i], NULL);
+ lazy_assert(result == 0);
+ }
+}
+
+/*
+ * free_indexer_resources() frees all of the resources associated with
+ * struct __toku_indexer_internal
+ * assumes any previously freed items set the field pointer to NULL
+ */
+
+static void
+free_indexer_resources(DB_INDEXER *indexer) {
+ if ( indexer->i ) {
+ toku_mutex_destroy(&indexer->i->indexer_lock);
+ toku_mutex_destroy(&indexer->i->indexer_estimate_lock);
+ toku_destroy_dbt(&indexer->i->position_estimate);
+ if ( indexer->i->lec ) {
+ toku_le_cursor_close(indexer->i->lec);
+ }
+ if ( indexer->i->fnums ) {
+ toku_free(indexer->i->fnums);
+ indexer->i->fnums = NULL;
+ }
+ indexer_undo_do_destroy(indexer);
+ // indexer->i
+ toku_free(indexer->i);
+ indexer->i = NULL;
+ }
+}
+
+static void
+free_indexer(DB_INDEXER *indexer) {
+ if ( indexer ) {
+ free_indexer_resources(indexer);
+ toku_free(indexer);
+ indexer = NULL;
+ }
+}
+
+void
+toku_indexer_lock(DB_INDEXER* indexer) {
+ toku_mutex_lock(&indexer->i->indexer_lock);
+}
+
+void
+toku_indexer_unlock(DB_INDEXER* indexer) {
+ toku_mutex_unlock(&indexer->i->indexer_lock);
+}
+
+// a shortcut call
+//
+// a cheap(er) call to see if a key must be inserted
+// into the DB. If true, then we know we have to insert.
+// If false, then we don't know, and have to check again
+// after grabbing the indexer lock
+bool
+toku_indexer_may_insert(DB_INDEXER* indexer, const DBT* key) {
+ bool may_insert = false;
+ toku_mutex_lock(&indexer->i->indexer_estimate_lock);
+
+ // if we have no position estimate, we can't tell, so return false
+ if (indexer->i->position_estimate.data == nullptr) {
+ may_insert = false;
+ } else {
+ DB *db = indexer->i->src_db;
+ const toku::comparator &cmp = toku_ft_get_comparator(db->i->ft_handle);
+ int c = cmp(&indexer->i->position_estimate, key);
+
+ // if key > position_estimate, then we know the indexer cursor
+ // is past key, and we can safely say that associated values of
+ // key must be inserted into the indexer's db
+ may_insert = c < 0;
+ }
+
+ toku_mutex_unlock(&indexer->i->indexer_estimate_lock);
+ return may_insert;
+}
+
+void
+toku_indexer_update_estimate(DB_INDEXER* indexer) {
+ toku_mutex_lock(&indexer->i->indexer_estimate_lock);
+ toku_le_cursor_update_estimate(indexer->i->lec, &indexer->i->position_estimate);
+ toku_mutex_unlock(&indexer->i->indexer_estimate_lock);
+}
+
+// forward declare the test-only wrapper function for undo-do
+static int test_indexer_undo_do(DB_INDEXER *indexer, DB *hotdb, DBT* key, ULEHANDLE ule);
+
+int
+toku_indexer_create_indexer(DB_ENV *env,
+ DB_TXN *txn,
+ DB_INDEXER **indexerp,
+ DB *src_db,
+ int N,
+ DB *dest_dbs[/*N*/],
+ uint32_t db_flags[/*N*/] UU(),
+ uint32_t indexer_flags)
+{
+ int rval;
+ DB_INDEXER *indexer = 0; // set later when created
+ HANDLE_READ_ONLY_TXN(txn);
+
+ *indexerp = NULL;
+
+ XCALLOC(indexer); // init to all zeroes (thus initializing the error_callback and poll_func)
+ if ( !indexer ) { rval = ENOMEM; goto create_exit; }
+ XCALLOC(indexer->i); // init to all zeroes (thus initializing all pointers to NULL)
+ if ( !indexer->i ) { rval = ENOMEM; goto create_exit; }
+
+ indexer->i->env = env;
+ indexer->i->txn = txn;
+ indexer->i->src_db = src_db;
+ indexer->i->N = N;
+ indexer->i->dest_dbs = dest_dbs;
+ indexer->i->indexer_flags = indexer_flags;
+ indexer->i->loop_mod = 1000; // call poll_func every 1000 rows
+ indexer->i->estimated_rows = 0;
+ indexer->i->undo_do = test_indexer_undo_do; // TEST export the undo do function
+
+ XCALLOC_N(N, indexer->i->fnums);
+ if ( !indexer->i->fnums ) { rval = ENOMEM; goto create_exit; }
+ for(int i=0;i<indexer->i->N;i++) {
+ indexer->i->fnums[i] = toku_cachefile_filenum(db_struct_i(dest_dbs[i])->ft_handle->ft->cf);
+ }
+ indexer->i->filenums.num = N;
+ indexer->i->filenums.filenums = indexer->i->fnums;
+ indexer->i->test_only_flags = 0; // for test use only
+
+ indexer->set_error_callback = toku_indexer_set_error_callback;
+ indexer->set_poll_function = toku_indexer_set_poll_function;
+ indexer->build = build_index;
+ indexer->close = close_indexer;
+ indexer->abort = abort_indexer;
+
+ toku_mutex_init(&indexer->i->indexer_lock, NULL);
+ toku_mutex_init(&indexer->i->indexer_estimate_lock, NULL);
+ toku_init_dbt(&indexer->i->position_estimate);
+
+ //
+ // create and close a dummy loader to get redirection going for the hot indexer
+ // This way, if the hot index aborts, but other transactions have references to the
+ // underlying FT, then those transactions can do dummy operations on the FT
+ // while the DB gets redirected back to an empty dictionary
+ //
+ {
+ DB_LOADER* loader = NULL;
+ rval = toku_loader_create_loader(env, txn, &loader, NULL, N, &dest_dbs[0], NULL, NULL, DB_PRELOCKED_WRITE | LOADER_DISALLOW_PUTS, true);
+ if (rval) {
+ goto create_exit;
+ }
+ rval = loader->close(loader);
+ if (rval) {
+ goto create_exit;
+ }
+ }
+
+ // create and initialize the leafentry cursor
+ rval = toku_le_cursor_create(&indexer->i->lec, db_struct_i(src_db)->ft_handle, db_txn_struct_i(txn)->tokutxn);
+ if ( !indexer->i->lec ) { goto create_exit; }
+
+ // 2954: add recovery and rollback entries
+ LSN hot_index_lsn; // not used (yet)
+ TOKUTXN ttxn;
+ ttxn = db_txn_struct_i(txn)->tokutxn;
+ FILENUMS filenums;
+ filenums = indexer->i->filenums;
+ toku_multi_operation_client_lock();
+ toku_ft_hot_index(NULL, ttxn, filenums, 1, &hot_index_lsn);
+ toku_multi_operation_client_unlock();
+
+ if (rval == 0) {
+ rval = associate_indexer_with_hot_dbs(indexer, dest_dbs, N);
+ }
+create_exit:
+ if ( rval == 0 ) {
+
+ indexer_undo_do_init(indexer);
+
+ *indexerp = indexer;
+
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(INDEXER_CREATE), 1);
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(INDEXER_CURRENT), 1);
+ if ( STATUS_VALUE(INDEXER_CURRENT) > STATUS_VALUE(INDEXER_MAX) )
+ STATUS_VALUE(INDEXER_MAX) = STATUS_VALUE(INDEXER_CURRENT); // NOT WORTH A LOCK TO MAKE THREADSAFE), may be inaccurate
+
+ } else {
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(INDEXER_CREATE_FAIL), 1);
+ free_indexer(indexer);
+ }
+
+ return rval;
+}
+
+int
+toku_indexer_set_poll_function(DB_INDEXER *indexer,
+ int (*poll_func)(void *poll_extra,
+ float progress),
+ void *poll_extra)
+{
+ invariant(indexer != NULL);
+ indexer->i->poll_func = poll_func;
+ indexer->i->poll_extra = poll_extra;
+ return 0;
+}
+
+int
+toku_indexer_set_error_callback(DB_INDEXER *indexer,
+ void (*error_cb)(DB *db, int i, int err,
+ DBT *key, DBT *val,
+ void *error_extra),
+ void *error_extra)
+{
+ invariant(indexer != NULL);
+ indexer->i->error_callback = error_cb;
+ indexer->i->error_extra = error_extra;
+ return 0;
+}
+
+// a key is to the right of the indexer's cursor if it compares
+// greater than the current le cursor position.
+bool
+toku_indexer_should_insert_key(DB_INDEXER *indexer, const DBT *key) {
+ // the hot indexer runs from the end to the beginning, it gets the largest keys first
+ //
+ // if key is less than indexer's position, then we should NOT insert it because
+ // the indexer will get to it. If it is greater or equal, that means the indexer
+ // has already processed the key, and will not get to it, therefore, we need
+ // to handle it
+ return toku_le_cursor_is_key_greater_or_equal(indexer->i->lec, key);
+}
+
+// initialize provisional info by allocating enough space to hold provisional
+// ids, states, and txns for each of the provisional entries in the ule. the
+// ule and le remain owned by the caller, not this struct.
+static void
+ule_prov_info_init(struct ule_prov_info *prov_info, const void* key, uint32_t keylen, LEAFENTRY le, ULEHANDLE ule) {
+ prov_info->le = le;
+ prov_info->ule = ule;
+ prov_info->keylen = keylen;
+ prov_info->key = toku_xmalloc(keylen);
+ memcpy(prov_info->key, key, keylen);
+ prov_info->num_provisional = ule_get_num_provisional(ule);
+ prov_info->num_committed = ule_get_num_committed(ule);
+ uint32_t n = prov_info->num_provisional;
+ if (n > 0) {
+ XMALLOC_N(n, prov_info->prov_ids);
+ XMALLOC_N(n, prov_info->prov_states);
+ XMALLOC_N(n, prov_info->prov_txns);
+ }
+}
+
+// clean up anything possibly created by ule_prov_info_init()
+static void
+ule_prov_info_destroy(struct ule_prov_info *prov_info) {
+ if (prov_info->num_provisional > 0) {
+ toku_free(prov_info->prov_ids);
+ toku_free(prov_info->prov_states);
+ toku_free(prov_info->prov_txns);
+ } else {
+ // nothing to free if there was nothing provisional
+ invariant(prov_info->prov_ids == NULL);
+ invariant(prov_info->prov_states == NULL);
+ invariant(prov_info->prov_txns == NULL);
+ }
+}
+
+static void
+indexer_fill_prov_info(DB_INDEXER *indexer, struct ule_prov_info *prov_info) {
+ ULEHANDLE ule = prov_info->ule;
+ uint32_t num_provisional = prov_info->num_provisional;
+ uint32_t num_committed = prov_info->num_committed;
+ TXNID *prov_ids = prov_info->prov_ids;
+ TOKUTXN_STATE *prov_states = prov_info->prov_states;
+ TOKUTXN *prov_txns = prov_info->prov_txns;
+
+ // don't both grabbing the txn manager lock if we don't
+ // have any provisional txns to record
+ if (num_provisional == 0) {
+ return;
+ }
+
+ // handle test case first
+ if (indexer->i->test_xid_state) {
+ for (uint32_t i = 0; i < num_provisional; i++) {
+ UXRHANDLE uxr = ule_get_uxr(ule, num_committed + i);
+ prov_ids[i] = uxr_get_txnid(uxr);
+ prov_states[i] = indexer->i->test_xid_state(indexer, prov_ids[i]);
+ prov_txns[i] = NULL;
+ }
+ return;
+ }
+
+ // hold the txn manager lock while we inspect txn state
+ // and pin some live txns
+ DB_ENV *env = indexer->i->env;
+ TXN_MANAGER txn_manager = toku_logger_get_txn_manager(env->i->logger);
+ TXNID parent_xid = uxr_get_txnid(ule_get_uxr(ule, num_committed));
+
+ // let's first initialize things to defaults
+ for (uint32_t i = 0; i < num_provisional; i++) {
+ UXRHANDLE uxr = ule_get_uxr(ule, num_committed + i);
+ prov_ids[i] = uxr_get_txnid(uxr);
+ prov_txns[i] = NULL;
+ prov_states[i] = TOKUTXN_RETIRED;
+ }
+
+ toku_txn_manager_suspend(txn_manager);
+ TXNID_PAIR root_xid_pair = {.parent_id64=parent_xid, .child_id64 = TXNID_NONE};
+ TOKUTXN root_txn = NULL;
+ toku_txn_manager_id2txn_unlocked(
+ txn_manager,
+ root_xid_pair,
+ &root_txn
+ );
+ if (root_txn == NULL) {
+ toku_txn_manager_resume(txn_manager);
+ return; //everything is retired in this case, the default
+ }
+ prov_txns[0] = root_txn;
+ prov_states[0] = toku_txn_get_state(root_txn);
+ toku_txn_lock_state(root_txn);
+ prov_states[0] = toku_txn_get_state(root_txn);
+ if (prov_states[0] == TOKUTXN_LIVE || prov_states[0] == TOKUTXN_PREPARING) {
+ // pin this live txn so it can't commit or abort until we're done with it
+ toku_txn_pin_live_txn_unlocked(root_txn);
+ }
+ toku_txn_unlock_state(root_txn);
+
+ root_txn->child_manager->suspend();
+ for (uint32_t i = 1; i < num_provisional; i++) {
+ UXRHANDLE uxr = ule_get_uxr(ule, num_committed + i);
+ TXNID child_id = uxr_get_txnid(uxr);
+ TOKUTXN txn = NULL;
+
+ TXNID_PAIR txnid_pair = {.parent_id64 = parent_xid, .child_id64 = child_id};
+ root_txn->child_manager->find_tokutxn_by_xid_unlocked(txnid_pair, &txn);
+ prov_txns[i] = txn;
+ if (txn) {
+ toku_txn_lock_state(txn);
+ prov_states[i] = toku_txn_get_state(txn);
+ if (prov_states[i] == TOKUTXN_LIVE || prov_states[i] == TOKUTXN_PREPARING) {
+ // pin this live txn so it can't commit or abort until we're done with it
+ toku_txn_pin_live_txn_unlocked(txn);
+ }
+ toku_txn_unlock_state(txn);
+ }
+ else {
+ prov_states[i] = TOKUTXN_RETIRED;
+ }
+ }
+ root_txn->child_manager->resume();
+ toku_txn_manager_resume(txn_manager);
+}
+
+struct le_cursor_extra {
+ DB_INDEXER *indexer;
+ struct ule_prov_info *prov_info;
+};
+
+// cursor callback, so its synchronized with other db operations using
+// cachetable pair locks. because no txn can commit on this db, read
+// the provisional info for the newly read ule.
+static int
+le_cursor_callback(uint32_t keylen, const void *key, uint32_t UU(vallen), const void *val, void *extra, bool lock_only) {
+ if (lock_only || val == NULL) {
+ ; // do nothing if only locking. do nothing if val==NULL, means DB_NOTFOUND
+ } else {
+ struct le_cursor_extra *CAST_FROM_VOIDP(cursor_extra, extra);
+ struct ule_prov_info *prov_info = cursor_extra->prov_info;
+ // the val here is a leafentry. ule_create does not copy the entire
+ // contents of the leafentry it is given into its own buffers, so we
+ // must allocate space for a leafentry and keep it around with the ule.
+ LEAFENTRY CAST_FROM_VOIDP(le, toku_xmemdup(val, vallen));
+ ULEHANDLE ule = toku_ule_create(le);
+ invariant(ule);
+ // when we initialize prov info, we also pass in the leafentry and ule
+ // pointers so the caller can access them later. it's their job to free
+ // them when they're not needed.
+ ule_prov_info_init(prov_info, key, keylen, le, ule);
+ indexer_fill_prov_info(cursor_extra->indexer, prov_info);
+ }
+ return 0;
+}
+
+// get the next ule and fill out its provisional info in the
+// prov_info struct provided. caller is responsible for cleaning
+// up the ule info after it's done.
+static int
+get_next_ule_with_prov_info(DB_INDEXER *indexer, struct ule_prov_info *prov_info) {
+ struct le_cursor_extra extra = {
+ .indexer = indexer,
+ .prov_info = prov_info,
+ };
+ int r = toku_le_cursor_next(indexer->i->lec, le_cursor_callback, &extra);
+ return r;
+}
+
+static int
+build_index(DB_INDEXER *indexer) {
+ int result = 0;
+
+ bool done = false;
+ for (uint64_t loop_count = 0; !done; loop_count++) {
+
+ toku_indexer_lock(indexer);
+ // grab the multi operation lock because we will be injecting messages
+ // grab it here because we must hold it before
+ // trying to pin any live transactions, as discovered by #5775
+ toku_multi_operation_client_lock();
+
+ // grab the next leaf entry and get its provisional info. we'll
+ // need the provisional info for the undo-do algorithm, and we get
+ // it here so it can be read atomically with respect to txn commit
+ // and abort. the atomicity comes from the root-to-leaf path pinned
+ // by the query and in the getf callback function
+ //
+ // this allocates space for the prov info, so we have to destroy it
+ // when we're done.
+ struct ule_prov_info prov_info;
+ memset(&prov_info, 0, sizeof(prov_info));
+ result = get_next_ule_with_prov_info(indexer, &prov_info);
+
+ if (result != 0) {
+ invariant(prov_info.ule == NULL);
+ done = true;
+ if (result == DB_NOTFOUND) {
+ result = 0; // all done, normal way to exit loop successfully
+ }
+ }
+ else {
+ invariant(prov_info.le);
+ invariant(prov_info.ule);
+ for (int which_db = 0; (which_db < indexer->i->N) && (result == 0); which_db++) {
+ DB *db = indexer->i->dest_dbs[which_db];
+ DBT_ARRAY *hot_keys = &indexer->i->hot_keys[which_db];
+ DBT_ARRAY *hot_vals = &indexer->i->hot_vals[which_db];
+ result = indexer_undo_do(indexer, db, &prov_info, hot_keys, hot_vals);
+ if ((result != 0) && (indexer->i->error_callback != NULL)) {
+ // grab the key and call the error callback
+ DBT key; toku_init_dbt_flags(&key, DB_DBT_REALLOC);
+ toku_dbt_set(prov_info.keylen, prov_info.key, &key, NULL);
+ indexer->i->error_callback(db, which_db, result, &key, NULL, indexer->i->error_extra);
+ toku_destroy_dbt(&key);
+ }
+ }
+ // the leafentry and ule are not owned by the prov_info,
+ // and are still our responsibility to free
+ toku_free(prov_info.le);
+ toku_free(prov_info.key);
+ toku_ule_free(prov_info.ule);
+ }
+
+ toku_multi_operation_client_unlock();
+ toku_indexer_unlock(indexer);
+ ule_prov_info_destroy(&prov_info);
+
+ if (result == 0) {
+ result = maybe_call_poll_func(indexer, loop_count);
+ }
+ if (result != 0) {
+ done = true;
+ }
+ }
+
+ // post index creation cleanup
+ // - optimize?
+ // - garbage collect?
+ // - unique checks?
+
+ if ( result == 0 ) {
+ // Perform a checkpoint so that all of the indexing makes it to disk before continuing.
+ // Otherwise indexing would not be crash-safe becasue none of the undo-do messages are in the recovery log.
+ DB_ENV *env = indexer->i->env;
+ CHECKPOINTER cp = toku_cachetable_get_checkpointer(env->i->cachetable);
+ toku_checkpoint(cp, env->i->logger, NULL, NULL, NULL, NULL, INDEXER_CHECKPOINT);
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(INDEXER_BUILD), 1);
+ } else {
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(INDEXER_BUILD_FAIL), 1);
+ }
+
+ return result;
+}
+
+// Clients must not operate on any of the hot dbs concurrently with close
+static int
+close_indexer(DB_INDEXER *indexer) {
+ int r = 0;
+ (void) toku_sync_fetch_and_sub(&STATUS_VALUE(INDEXER_CURRENT), 1);
+
+ // Disassociate the indexer from the hot db and free_indexer
+ disassociate_indexer_from_hot_dbs(indexer);
+ free_indexer(indexer);
+
+ if ( r == 0 ) {
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(INDEXER_CLOSE), 1);
+ } else {
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(INDEXER_CLOSE_FAIL), 1);
+ }
+ return r;
+}
+
+// Clients must not operate on any of the hot dbs concurrently with abort
+static int
+abort_indexer(DB_INDEXER *indexer) {
+ (void) toku_sync_fetch_and_sub(&STATUS_VALUE(INDEXER_CURRENT), 1);
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(INDEXER_ABORT), 1);
+ // Disassociate the indexer from the hot db and free_indexer
+ disassociate_indexer_from_hot_dbs(indexer);
+ free_indexer(indexer);
+ return 0;
+}
+
+
+// derived from the handlerton's estimate_num_rows()
+static int
+update_estimated_rows(DB_INDEXER *indexer) {
+ int error;
+ DB_TXN *txn = NULL;
+ DB_ENV *db_env = indexer->i->env;
+ error = db_env->txn_begin(db_env, 0, &txn, DB_READ_UNCOMMITTED);
+ if (error == 0) {
+ DB_BTREE_STAT64 stats;
+ DB *db = indexer->i->src_db;
+ error = db->stat64(db, txn, &stats);
+ if (error == 0) {
+ indexer->i->estimated_rows = stats.bt_ndata;
+ }
+ txn->commit(txn, 0);
+ }
+ return error;
+}
+
+static int
+maybe_call_poll_func(DB_INDEXER *indexer, uint64_t loop_count) {
+ int result = 0;
+ if ( indexer->i->poll_func != NULL && ( loop_count % indexer->i->loop_mod ) == 0 ) {
+ int r __attribute__((unused)) = update_estimated_rows(indexer);
+ // what happens if estimate_rows fails?
+ // - currently does not modify estimate, which is probably sufficient
+ float progress;
+ if ( indexer->i->estimated_rows == 0 || loop_count > indexer->i->estimated_rows)
+ progress = 1.0;
+ else
+ progress = (float)loop_count / (float)indexer->i->estimated_rows;
+ result = indexer->i->poll_func(indexer->i->poll_extra, progress);
+ }
+ return result;
+}
+
+
+// this allows us to force errors under test. Flags are defined in indexer.h
+void
+toku_indexer_set_test_only_flags(DB_INDEXER *indexer, int flags) {
+ invariant(indexer != NULL);
+ indexer->i->test_only_flags = flags;
+}
+
+// this allows us to call the undo do function in tests using
+// a convenience wrapper that gets and destroys the ule's prov info
+static int
+test_indexer_undo_do(DB_INDEXER *indexer, DB *hotdb, DBT* key, ULEHANDLE ule) {
+ int which_db;
+ for (which_db = 0; which_db < indexer->i->N; which_db++) {
+ if (indexer->i->dest_dbs[which_db] == hotdb) {
+ break;
+ }
+ }
+ if (which_db == indexer->i->N) {
+ return EINVAL;
+ }
+ struct ule_prov_info prov_info;
+ memset(&prov_info, 0, sizeof(prov_info));
+ // pass null for the leafentry - we don't need it, neither does the info
+ ule_prov_info_init(&prov_info, key->data, key->size, NULL, ule); // mallocs prov_info->key, owned by this function
+ indexer_fill_prov_info(indexer, &prov_info);
+ DBT_ARRAY *hot_keys = &indexer->i->hot_keys[which_db];
+ DBT_ARRAY *hot_vals = &indexer->i->hot_vals[which_db];
+ int r = indexer_undo_do(indexer, hotdb, &prov_info, hot_keys, hot_vals);
+ toku_free(prov_info.key);
+ ule_prov_info_destroy(&prov_info);
+ return r;
+}
+
+DB *
+toku_indexer_get_src_db(DB_INDEXER *indexer) {
+ return indexer->i->src_db;
+}
+
+
+#undef STATUS_VALUE
+
diff --git a/storage/tokudb/PerconaFT/src/indexer.h b/storage/tokudb/PerconaFT/src/indexer.h
new file mode 100644
index 00000000000..dc0c290935e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/indexer.h
@@ -0,0 +1,125 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+// locking and unlocking functions to synchronize cursor position with
+// XXX_multiple APIs
+void toku_indexer_lock(DB_INDEXER* indexer);
+
+void toku_indexer_unlock(DB_INDEXER* indexer);
+bool toku_indexer_may_insert(DB_INDEXER* indexer, const DBT* key);
+void toku_indexer_update_estimate(DB_INDEXER* indexer);
+
+// The indexer populates multiple destination db's from the contents of one source db.
+// While the indexes are being built by the indexer, the application may continue to
+// change the contents of the source db. The changes will be reflected into the destination
+// db's by the indexer.
+//
+// Each indexer references one source db.
+// A source db may have multiple indexers referencing it.
+// Each indexer references one or more destination db's.
+// Each destination db references the one and only indexer that is building it.
+//
+// env must be set to the YDB environment
+// txn must be set to the transaction under which the indexer will run
+// *indexer is set to the address of the indexer object returned by the create function
+// src_db is the source db
+// N is the number of destination db's
+// dest_dbs is an array of pointers to destination db's
+// db_flags is currently unused
+// indexer_flags is currently unused
+//
+// Returns 0 if the indexer has been created and sets *indexer to the indexer object.
+// If an error occurred while creating the indexer object, a non-zero error number is returned.
+//
+// Clients must not operate on any of the dest_dbs concurrently with create_indexer();
+int toku_indexer_create_indexer(DB_ENV *env,
+ DB_TXN *txn,
+ DB_INDEXER **indexer,
+ DB *src_db,
+ int N,
+ DB *dest_dbs[/*N*/],
+ uint32_t db_flags[/*N*/],
+ uint32_t indexer_flags) __attribute__((__visibility__("default")));
+
+// Set the indexer poll function
+int toku_indexer_set_poll_function(DB_INDEXER *indexer,
+ int (*poll_function)(void *poll_extra,
+ float progress),
+ void *poll_extra);
+
+// Set the indexer error callback
+int toku_indexer_set_error_callback(DB_INDEXER *indexer,
+ void (*error_cb)(DB *db, int i, int err,
+ DBT *key, DBT *val,
+ void *error_extra),
+ void *error_extra);
+
+// Is the key right of the indexer's leaf entry cursor?
+// Returns true if right of le_cursor
+// Returns false if left or equal to le_cursor
+bool toku_indexer_should_insert_key(DB_INDEXER *indexer, const DBT *key);
+
+// Get the indexer's source db
+DB *toku_indexer_get_src_db(DB_INDEXER *indexer);
+
+// TEST set the indexer's test flags
+extern "C" void toku_indexer_set_test_only_flags(DB_INDEXER *indexer, int flags) __attribute__((__visibility__("default")));
+
+#define INDEXER_TEST_ONLY_ERROR_CALLBACK 1
+
+typedef enum {
+ INDEXER_CREATE = 0, // number of indexers successfully created
+ INDEXER_CREATE_FAIL, // number of calls to toku_indexer_create_indexer() that failed
+ INDEXER_BUILD, // number of calls to indexer->build() succeeded
+ INDEXER_BUILD_FAIL, // number of calls to indexer->build() failed
+ INDEXER_CLOSE, // number of calls to indexer->close() that succeeded
+ INDEXER_CLOSE_FAIL, // number of calls to indexer->close() that failed
+ INDEXER_ABORT, // number of calls to indexer->abort()
+ INDEXER_CURRENT, // number of indexers currently in existence
+ INDEXER_MAX, // max number of indexers that ever existed simultaneously
+ INDEXER_STATUS_NUM_ROWS
+} indexer_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[INDEXER_STATUS_NUM_ROWS];
+} INDEXER_STATUS_S, *INDEXER_STATUS;
+
+void toku_indexer_get_status(INDEXER_STATUS s);
diff --git a/storage/tokudb/PerconaFT/src/loader.cc b/storage/tokudb/PerconaFT/src/loader.cc
new file mode 100644
index 00000000000..4a195d1158e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/loader.cc
@@ -0,0 +1,518 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/*
+ * The loader
+ */
+
+#include <toku_portability.h>
+#include <portability/toku_atomic.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <ft/ft.h>
+#include <ft/loader/loader.h>
+#include <ft/cachetable/checkpoint.h>
+
+#include "ydb-internal.h"
+#include "ydb_db.h"
+#include "ydb_load.h"
+
+#include "loader.h"
+#include <util/status.h>
+
+enum {MAX_FILE_SIZE=256};
+
+///////////////////////////////////////////////////////////////////////////////////
+// Engine status
+//
+// Status is intended for display to humans to help understand system behavior.
+// It does not need to be perfectly thread-safe.
+
+static LOADER_STATUS_S loader_status;
+
+#define STATUS_INIT(k,c,t,l,inc) TOKUFT_STATUS_INIT(loader_status, k, c, t, "loader: " l, inc)
+
+static void
+status_init(void) {
+ // Note, this function initializes the keyname, type, and legend fields.
+ // Value fields are initialized to zero by compiler.
+ STATUS_INIT(LOADER_CREATE, LOADER_NUM_CREATED, UINT64, "number of loaders successfully created", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ STATUS_INIT(LOADER_CREATE_FAIL, nullptr, UINT64, "number of calls to toku_loader_create_loader() that failed", TOKU_ENGINE_STATUS);
+ STATUS_INIT(LOADER_PUT, nullptr, UINT64, "number of calls to loader->put() succeeded", TOKU_ENGINE_STATUS);
+ STATUS_INIT(LOADER_PUT_FAIL, nullptr, UINT64, "number of calls to loader->put() failed", TOKU_ENGINE_STATUS);
+ STATUS_INIT(LOADER_CLOSE, nullptr, UINT64, "number of calls to loader->close() that succeeded", TOKU_ENGINE_STATUS);
+ STATUS_INIT(LOADER_CLOSE_FAIL, nullptr, UINT64, "number of calls to loader->close() that failed", TOKU_ENGINE_STATUS);
+ STATUS_INIT(LOADER_ABORT, nullptr, UINT64, "number of calls to loader->abort()", TOKU_ENGINE_STATUS);
+ STATUS_INIT(LOADER_CURRENT, LOADER_NUM_CURRENT, UINT64, "number of loaders currently in existence", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ STATUS_INIT(LOADER_MAX, LOADER_NUM_MAX, UINT64, "max number of loaders that ever existed simultaneously", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ loader_status.initialized = true;
+}
+#undef STATUS_INIT
+
+void
+toku_loader_get_status(LOADER_STATUS statp) {
+ if (!loader_status.initialized)
+ status_init();
+ *statp = loader_status;
+}
+
+#define STATUS_VALUE(x) loader_status.status[x].value.num
+
+
+struct __toku_loader_internal {
+ DB_ENV *env;
+ DB_TXN *txn;
+ FTLOADER ft_loader;
+ int N;
+ DB **dbs; /* [N] */
+ DB *src_db;
+ uint32_t *db_flags;
+ uint32_t *dbt_flags;
+ uint32_t loader_flags;
+ void (*error_callback)(DB *db, int i, int err, DBT *key, DBT *val, void *error_extra);
+ void *error_extra;
+ int (*poll_func)(void *poll_extra, float progress);
+ void *poll_extra;
+ char *temp_file_template;
+
+ DBT err_key; /* error key */
+ DBT err_val; /* error val */
+ int err_i; /* error i */
+ int err_errno;
+
+ char **inames_in_env; /* [N] inames of new files to be created */
+};
+
+static void free_inames(char **inames, int n) {
+ for (int i = 0; i < n; i++) {
+ toku_free(inames[i]);
+ }
+ toku_free(inames);
+}
+
+/*
+ * free_loader_resources() frees all of the resources associated with
+ * struct __toku_loader_internal
+ * assumes any previously freed items set the field pointer to NULL
+ * Requires that the ft_loader is closed or destroyed before calling this function.
+ */
+static void free_loader_resources(DB_LOADER *loader)
+{
+ if ( loader->i ) {
+ toku_destroy_dbt(&loader->i->err_key);
+ toku_destroy_dbt(&loader->i->err_val);
+
+ if (loader->i->inames_in_env) {
+ free_inames(loader->i->inames_in_env, loader->i->N);
+ loader->i->inames_in_env = nullptr;
+ }
+ toku_free(loader->i->temp_file_template);
+ loader->i->temp_file_template = nullptr;
+
+ // loader->i
+ toku_free(loader->i);
+ loader->i = nullptr;
+ }
+}
+
+static void free_loader(DB_LOADER *loader)
+{
+ if ( loader ) free_loader_resources(loader);
+ toku_free(loader);
+}
+
+static const char *loader_temp_prefix = "tokuld"; // #2536
+static const char *loader_temp_suffix = "XXXXXX";
+
+static int ft_loader_close_and_redirect(DB_LOADER *loader) {
+ int r;
+ // use the bulk loader
+ // in case you've been looking - here is where the real work is done!
+ r = toku_ft_loader_close(loader->i->ft_loader,
+ loader->i->error_callback, loader->i->error_extra,
+ loader->i->poll_func, loader->i->poll_extra);
+ if ( r==0 ) {
+ for (int i=0; i<loader->i->N; i++) {
+ toku_multi_operation_client_lock(); //Must hold MO lock for dictionary_redirect.
+ r = toku_dictionary_redirect(loader->i->inames_in_env[i],
+ loader->i->dbs[i]->i->ft_handle,
+ db_txn_struct_i(loader->i->txn)->tokutxn);
+ toku_multi_operation_client_unlock();
+ if ( r!=0 ) break;
+ }
+ }
+ return r;
+}
+
+
+// loader_flags currently has the following flags:
+// LOADER_DISALLOW_PUTS loader->put is not allowed.
+// Loader is only being used for its side effects
+// DB_PRELOCKED_WRITE Table lock is already held, no need to relock.
+int
+toku_loader_create_loader(DB_ENV *env,
+ DB_TXN *txn,
+ DB_LOADER **blp,
+ DB *src_db,
+ int N,
+ DB *dbs[],
+ uint32_t db_flags[/*N*/],
+ uint32_t dbt_flags[/*N*/],
+ uint32_t loader_flags,
+ bool check_empty) {
+ int rval;
+ HANDLE_READ_ONLY_TXN(txn);
+ DB_TXN *loader_txn = nullptr;
+
+ *blp = NULL; // set later when created
+
+ DB_LOADER *loader = NULL;
+ bool puts_allowed = !(loader_flags & LOADER_DISALLOW_PUTS);
+ bool compress_intermediates = (loader_flags & LOADER_COMPRESS_INTERMEDIATES) != 0;
+ XCALLOC(loader); // init to all zeroes (thus initializing the error_callback and poll_func)
+ XCALLOC(loader->i); // init to all zeroes (thus initializing all pointers to NULL)
+
+ loader->i->env = env;
+ loader->i->txn = txn;
+ loader->i->N = N;
+ loader->i->dbs = dbs;
+ loader->i->src_db = src_db;
+ loader->i->db_flags = db_flags;
+ loader->i->dbt_flags = dbt_flags;
+ loader->i->loader_flags = loader_flags;
+ loader->i->temp_file_template = (char *)toku_malloc(MAX_FILE_SIZE);
+
+ int n = snprintf(loader->i->temp_file_template, MAX_FILE_SIZE, "%s/%s%s", env->i->real_tmp_dir, loader_temp_prefix, loader_temp_suffix);
+ if ( !(n>0 && n<MAX_FILE_SIZE) ) {
+ rval = ENAMETOOLONG;
+ goto create_exit;
+ }
+
+ toku_init_dbt(&loader->i->err_key);
+ toku_init_dbt(&loader->i->err_val);
+ loader->i->err_i = 0;
+ loader->i->err_errno = 0;
+
+ loader->set_error_callback = toku_loader_set_error_callback;
+ loader->set_poll_function = toku_loader_set_poll_function;
+ loader->put = toku_loader_put;
+ loader->close = toku_loader_close;
+ loader->abort = toku_loader_abort;
+
+ // lock tables and check empty
+ for(int i=0;i<N;i++) {
+ if (!(loader_flags&DB_PRELOCKED_WRITE)) {
+ rval = toku_db_pre_acquire_table_lock(dbs[i], txn);
+ if (rval!=0) {
+ goto create_exit;
+ }
+ }
+ if (check_empty) {
+ bool empty = toku_ft_is_empty_fast(dbs[i]->i->ft_handle);
+ if (!empty) {
+ rval = ENOTEMPTY;
+ goto create_exit;
+ }
+ }
+ }
+
+ {
+ if (env->i->open_flags & DB_INIT_TXN) {
+ rval = env->txn_begin(env, txn, &loader_txn, 0);
+ if (rval) {
+ goto create_exit;
+ }
+ }
+
+ ft_compare_func compare_functions[N];
+ for (int i=0; i<N; i++) {
+ compare_functions[i] = env->i->bt_compare;
+ }
+
+ // time to open the big kahuna
+ char **XMALLOC_N(N, new_inames_in_env);
+ for (int i = 0; i < N; i++) {
+ new_inames_in_env[i] = nullptr;
+ }
+ FT_HANDLE *XMALLOC_N(N, fts);
+ for (int i=0; i<N; i++) {
+ fts[i] = dbs[i]->i->ft_handle;
+ }
+ LSN load_lsn;
+ rval = locked_load_inames(env, loader_txn, N, dbs, new_inames_in_env, &load_lsn, puts_allowed);
+ if ( rval!=0 ) {
+ free_inames(new_inames_in_env, N);
+ toku_free(fts);
+ goto create_exit;
+ }
+ TOKUTXN ttxn = loader_txn ? db_txn_struct_i(loader_txn)->tokutxn : NULL;
+ rval = toku_ft_loader_open(&loader->i->ft_loader,
+ env->i->cachetable,
+ env->i->generate_row_for_put,
+ src_db,
+ N,
+ fts, dbs,
+ (const char **)new_inames_in_env,
+ compare_functions,
+ loader->i->temp_file_template,
+ load_lsn,
+ ttxn,
+ puts_allowed,
+ env->get_loader_memory_size(env),
+ compress_intermediates,
+ puts_allowed);
+ if ( rval!=0 ) {
+ free_inames(new_inames_in_env, N);
+ toku_free(fts);
+ goto create_exit;
+ }
+
+ loader->i->inames_in_env = new_inames_in_env;
+ toku_free(fts);
+
+ if (!puts_allowed) {
+ rval = ft_loader_close_and_redirect(loader);
+ assert_zero(rval);
+ loader->i->ft_loader = NULL;
+ // close the ft_loader and skip to the redirection
+ rval = 0;
+ }
+
+ rval = loader_txn->commit(loader_txn, 0);
+ assert_zero(rval);
+ loader_txn = nullptr;
+
+ rval = 0;
+ }
+ *blp = loader;
+ create_exit:
+ if (loader_txn) {
+ int r = loader_txn->abort(loader_txn);
+ assert_zero(r);
+ loader_txn = nullptr;
+ }
+ if (rval == 0) {
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(LOADER_CREATE), 1);
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(LOADER_CURRENT), 1);
+ if (STATUS_VALUE(LOADER_CURRENT) > STATUS_VALUE(LOADER_MAX) )
+ STATUS_VALUE(LOADER_MAX) = STATUS_VALUE(LOADER_CURRENT); // not worth a lock to make threadsafe, may be inaccurate
+ }
+ else {
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(LOADER_CREATE_FAIL), 1);
+ free_loader(loader);
+ }
+ return rval;
+}
+
+int toku_loader_set_poll_function(DB_LOADER *loader,
+ int (*poll_func)(void *extra, float progress),
+ void *poll_extra)
+{
+ invariant(loader != NULL);
+ loader->i->poll_func = poll_func;
+ loader->i->poll_extra = poll_extra;
+ return 0;
+}
+
+int toku_loader_set_error_callback(DB_LOADER *loader,
+ void (*error_cb)(DB *db, int i, int err, DBT *key, DBT *val, void *extra),
+ void *error_extra)
+{
+ invariant(loader != NULL);
+ loader->i->error_callback = error_cb;
+ loader->i->error_extra = error_extra;
+ return 0;
+}
+
+int toku_loader_put(DB_LOADER *loader, DBT *key, DBT *val)
+{
+ int r = 0;
+ int i = 0;
+ // err_i is unused now( always 0). How would we know which dictionary
+ // the error happens in? (put_multiple and toku_ft_loader_put do NOT report
+ // which dictionary).
+
+ // skip put if error already found
+ if ( loader->i->err_errno != 0 ) {
+ r = -1;
+ goto cleanup;
+ }
+
+ if (loader->i->loader_flags & LOADER_DISALLOW_PUTS) {
+ r = EINVAL;
+ goto cleanup;
+ }
+ else {
+ // calling toku_ft_loader_put without a lock assumes that the
+ // handlerton is guaranteeing single access to the loader
+ // future multi-threaded solutions may need to protect this call
+ r = toku_ft_loader_put(loader->i->ft_loader, key, val);
+ }
+ if ( r != 0 ) {
+ // spec says errors all happen on close
+ // - have to save key, val, errno (r) and i for duplicate callback
+ toku_clone_dbt(&loader->i->err_key, *key);
+ toku_clone_dbt(&loader->i->err_val, *val);
+
+ loader->i->err_i = i;
+ loader->i->err_errno = r;
+
+ // deliberately return content free value
+ // - must call error_callback to get error info
+ r = -1;
+ }
+ cleanup:
+ if (r==0)
+ STATUS_VALUE(LOADER_PUT)++; // executed too often to be worth making threadsafe
+ else
+ STATUS_VALUE(LOADER_PUT_FAIL)++;
+ return r;
+}
+
+static void redirect_loader_to_empty_dictionaries(DB_LOADER *loader) {
+ DB_LOADER* tmp_loader = NULL;
+ int r = toku_loader_create_loader(
+ loader->i->env,
+ loader->i->txn,
+ &tmp_loader,
+ loader->i->src_db,
+ loader->i->N,
+ loader->i->dbs,
+ loader->i->db_flags,
+ loader->i->dbt_flags,
+ LOADER_DISALLOW_PUTS,
+ false
+ );
+ lazy_assert_zero(r);
+ r = toku_loader_close(tmp_loader);
+}
+
+int toku_loader_close(DB_LOADER *loader)
+{
+ (void) toku_sync_fetch_and_sub(&STATUS_VALUE(LOADER_CURRENT), 1);
+ int r=0;
+ if ( loader->i->err_errno != 0 ) {
+ if ( loader->i->error_callback != NULL ) {
+ loader->i->error_callback(loader->i->dbs[loader->i->err_i], loader->i->err_i, loader->i->err_errno, &loader->i->err_key, &loader->i->err_val, loader->i->error_extra);
+ }
+ if (!(loader->i->loader_flags & LOADER_DISALLOW_PUTS ) ) {
+ r = toku_ft_loader_abort(loader->i->ft_loader, true);
+ redirect_loader_to_empty_dictionaries(loader);
+ }
+ else {
+ r = loader->i->err_errno;
+ }
+ }
+ else { // no error outstanding
+ if (!(loader->i->loader_flags & LOADER_DISALLOW_PUTS ) ) {
+ r = ft_loader_close_and_redirect(loader);
+ if (r) {
+ redirect_loader_to_empty_dictionaries(loader);
+ }
+ }
+ }
+ free_loader(loader);
+ if (r==0)
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(LOADER_CLOSE), 1);
+ else
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(LOADER_CLOSE_FAIL), 1);
+ return r;
+}
+
+int toku_loader_abort(DB_LOADER *loader)
+{
+ (void) toku_sync_fetch_and_sub(&STATUS_VALUE(LOADER_CURRENT), 1);
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(LOADER_ABORT), 1);
+ int r=0;
+ if ( loader->i->err_errno != 0 ) {
+ if ( loader->i->error_callback != NULL ) {
+ loader->i->error_callback(loader->i->dbs[loader->i->err_i], loader->i->err_i, loader->i->err_errno, &loader->i->err_key, &loader->i->err_val, loader->i->error_extra);
+ }
+ }
+
+ if (!(loader->i->loader_flags & LOADER_DISALLOW_PUTS) ) {
+ r = toku_ft_loader_abort(loader->i->ft_loader, true);
+ lazy_assert_zero(r);
+ }
+
+ redirect_loader_to_empty_dictionaries(loader);
+ free_loader(loader);
+ return r;
+}
+
+
+// find all of the files in the environments home directory that match the loader temp name and remove them
+int toku_loader_cleanup_temp_files(DB_ENV *env) {
+ int result;
+ struct dirent *de;
+ char * dir = env->i->real_tmp_dir;
+ DIR *d = opendir(dir);
+ if (d==0) {
+ result = get_error_errno(); goto exit;
+ }
+
+ result = 0;
+ while ((de = readdir(d))) {
+ int r = memcmp(de->d_name, loader_temp_prefix, strlen(loader_temp_prefix));
+ if (r == 0 && strlen(de->d_name) == strlen(loader_temp_prefix) + strlen(loader_temp_suffix)) {
+ int fnamelen = strlen(dir) + 1 + strlen(de->d_name) + 1; // One for the slash and one for the trailing NUL.
+ char fname[fnamelen];
+ int l = snprintf(fname, fnamelen, "%s/%s", dir, de->d_name);
+ assert(l+1 == fnamelen);
+ r = unlink(fname);
+ if (r!=0) {
+ result = get_error_errno();
+ perror("Trying to delete a rolltmp file");
+ }
+ }
+ }
+ {
+ int r = closedir(d);
+ if (r == -1)
+ result = get_error_errno();
+ }
+
+exit:
+ return result;
+}
+
+
+
+#undef STATUS_VALUE
+
diff --git a/storage/tokudb/PerconaFT/src/loader.h b/storage/tokudb/PerconaFT/src/loader.h
new file mode 100644
index 00000000000..c5e7a357899
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/loader.h
@@ -0,0 +1,156 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+/*
+Create and set up a loader.
+ - The loader will operate in environment env, and the load will happen within transaction txn.
+ - You must remember to close (or abort) the loader eventually (otherwise the resulting DBs will
+ not be valid, and you will have a memory leak).
+ - The number of databases to be loaded is N.
+ - The databases must already be open, and their handles are passed in in the array dbs.
+ In particular dbs[i] is the ith database.
+ - The loader will work right whether the DBs are empty or full. However if any of the DBs are not empty,
+ it may not be fast (e.g., the loader may simply perform DB->put() operations).
+ - For each row that is put into the loader, for i over each of the N DBs, generate_row is invoked on the
+ row to generate a secondary row.
+ - The DBTs passed to generate_row() will have the DB_DBT_REALLOC flag set, and the extract
+ function should realloc the memory passed in. The ulen field indicates how large the realloc'd
+ storage is, and if the extract function does perform a realloc it should update the ulen field.
+ - We require that the extract function always return 0.
+ - The generate_row function must be thread safe.
+ - Whenever two rows in dbs[i] need to be compared we use that db's comparison function. The
+ comparison function must be thread safe.
+ - DBs must have been set up with descriptors and comparison functions before calling any extract
+ or compare functions.
+ - loader_flags is used to specify loader specific behavior. For instance, LOADER_USE_PUTS tells the
+ loader to use traditional puts to save disk space while loading (at the cost of performance)
+ - The new loader is returned in *blp.
+
+ Modifies: :: env, txn, blp, and dbs.
+*/
+int toku_loader_create_loader(DB_ENV *env, DB_TXN *txn, DB_LOADER **blp, DB *src_db, int N, DB *dbs[/*N*/], uint32_t db_flags[/*N*/], uint32_t dbt_flags[/*N*/], uint32_t loader_flags, bool check_empty);
+
+
+/*
+Set a error callback.
+ - If at any point during the load the system notices that an error has occurred, error information is recorded.
+ - The callback function may be called during DB_LOADER->close() or DB_LOADER->abort(), at which time the error
+ information is returned.
+ - A key-val pair for one of the errors is returned along with the db, and the index i indicating which db
+ had the problem.
+ - This function will be called at most once (so even if there are many problems, only one call will be made.)
+ - If a duplicate is discovered, the error is DB_KEYEXIST.
+ - The error_extra passed at the time of set_error_callback is the value passed as the error_extra when an error occurs.
+*/
+int toku_loader_set_error_callback(DB_LOADER *loader, void (*error_cb)(DB *db, int i, int err, DBT *key, DBT *val, void *error_extra), void *error_extra);
+
+
+/*
+Set the polling function.
+ - During the DB_LOADER->close operation, the poll function is called periodically.
+ - If it ever returns nonzero, then the loader stops as soon as possible.
+ - The poll function is called with the extra passed into the loader create function.
+ - A floating point number is also returned, which ranges from 0.0 to 1.0, indicating progress. Progress of 0.0 means
+ no progress so far. Progress of 0.5 means that the job is about half done. Progress of 1.0 means the job is done.
+ The progress is just an estimate.
+*/
+int toku_loader_set_poll_function(DB_LOADER *loader, int (*poll_func)(void *poll_extra, float progress), void *poll_extra);
+
+
+/*
+Give a row to the loader.
+ - Returns zero if no error, non-zero if error.
+ - When the application sees a non-zero return from put(), it must abort(), which would then call the error callback.
+ - Once put() returns a non-zero value, any loader calls other than abort() are unsupported and will result in undefined behavior.
+*/
+int toku_loader_put(DB_LOADER *loader, DBT *key, DBT *val);
+
+
+/*
+Finish the load,
+ - Take all the rows and put them into dictionaries which are returned as open handlers through the original dbs array.
+ - Frees all the memory allocated by the loader.
+ - You may not use the loader handle again after calling close.
+ - The system will return an DB_KEYEXIST if in any of the resulting databases, there are two different rows with keys
+ that compare to be equal (and the duplicate callback function, if set, is called first).
+ - If the polling function has been set, the loader will periodically call the polling function. If the polling function
+ ever returns a nonzero value, then the loader will return immediately, possibly with the dictionaries in some
+ inconsistent state. (To get them to a consistent state, the enclosing transaction should abort.)
+ - To free the resources used by a loader, either DB_LOADER->close or DB_LOADER->abort must be called. After calling either
+ of those functions, no further loader operations can be performed with that loader.
+ - The DBs remain open after the loader is closed.
+*/
+int toku_loader_close(DB_LOADER *loader);
+
+
+/*
+Abort the load,
+ - Possibly leave none, some, or all of the puts in effect. You may need to abort the enclosing transaction to get
+ back to a sane state.
+ - To free the resources used by a loader, either DB_LOADER->close or DB_LOADER->abort must be called. After calling either
+ of those functions, no further loader operations can be performed with that loader.
+ - The DBs remain open after the loader is aborted.
+ */
+int toku_loader_abort(DB_LOADER *loader);
+
+// Remove any loader temp files that may have been left from a crashed system
+int toku_loader_cleanup_temp_files(DB_ENV *env);
+
+
+typedef enum {
+ LOADER_CREATE = 0, // number of loaders successfully created
+ LOADER_CREATE_FAIL, // number of calls to toku_loader_create_loader() that failed
+ LOADER_PUT, // number of calls to toku_loader_put() that succeeded
+ LOADER_PUT_FAIL, // number of calls to toku_loader_put() that failed
+ LOADER_CLOSE, // number of calls to toku_loader_close()
+ LOADER_CLOSE_FAIL, // number of calls to toku_loader_close() that failed
+ LOADER_ABORT, // number of calls to toku_loader_abort()
+ LOADER_CURRENT, // number of loaders currently in existence
+ LOADER_MAX, // max number of loaders that ever existed simultaneously
+ LOADER_STATUS_NUM_ROWS
+} loader_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[LOADER_STATUS_NUM_ROWS];
+} LOADER_STATUS_S, *LOADER_STATUS;
+
+
+void toku_loader_get_status(LOADER_STATUS s);
diff --git a/storage/tokudb/PerconaFT/src/tests/CMakeLists.txt b/storage/tokudb/PerconaFT/src/tests/CMakeLists.txt
new file mode 100644
index 00000000000..457dd9b96a9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/CMakeLists.txt
@@ -0,0 +1,491 @@
+set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS _GNU_SOURCE DONT_DEPRECATE_ERRNO)
+
+if(BUILD_TESTING OR BUILD_SRC_TESTS)
+ function(add_ydb_test bin)
+ add_toku_test(ydb ${bin} ${ARGN})
+ endfunction(add_ydb_test)
+ function(add_ydb_test_aux name bin)
+ add_toku_test_aux(ydb ${name} ${bin} ${ARGN})
+ endfunction(add_ydb_test_aux)
+
+ function(add_ydb_helgrind_test bin)
+ add_helgrind_test(ydb helgrind_${bin} $<TARGET_FILE:${bin}> ${ARGN})
+ endfunction(add_ydb_helgrind_test)
+ function(add_ydb_drd_test_aux name bin)
+ add_drd_test(ydb ${name} $<TARGET_FILE:${bin}> ${ARGN})
+ endfunction(add_ydb_drd_test_aux)
+ function(add_ydb_drd_test bin)
+ add_ydb_drd_test_aux(drd_${bin} ${bin} ${ARGN})
+ endfunction(add_ydb_drd_test)
+
+ file(GLOB transparent_upgrade_srcs RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" upgrade*.cc)
+
+ file(GLOB srcs RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" *.cc)
+ list(REMOVE_ITEM srcs ${transparent_upgrade_srcs})
+
+ set(recover_srcs test_log2.cc test_log3.cc test_log4.cc test_log5.cc test_log6.cc test_log7.cc test_log8.cc test_log9.cc test_log10.cc)
+ file(GLOB abortrecover_srcs RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" recover-*.cc)
+ file(GLOB loader_srcs RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" loader-*.cc)
+ file(GLOB stress_test_srcs RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" test_stress*.cc)
+ string(REGEX REPLACE "\\.cc(;|$)" ".recover\\1" recover_tests "${recover_srcs}")
+ string(REGEX REPLACE "\\.cc(;|$)" ".abortrecover\\1" abortrecover_tests "${abortrecover_srcs}")
+ string(REGEX REPLACE "\\.cc(;|$)" ".loader\\1" loader_tests "${loader_srcs}")
+ string(REGEX REPLACE "\\.cc(;|$)" ".tdb\\1" stress_tests "${stress_test_srcs}")
+
+ set(tdb_srcs ${srcs})
+ string(REGEX REPLACE "\\.cc(;|$)" ".tdb\\1" tdb_bins "${tdb_srcs}")
+ list(REMOVE_ITEM tdb_srcs ${abortrecover_srcs} ${loader_srcs})
+ string(REGEX REPLACE "\\.cc(;|$)" ".tdb\\1" tdb_tests "${tdb_srcs}")
+
+ set(tdb_tests_that_should_fail
+ test_db_no_env.tdb
+ test_log8.recover
+ test_log9.recover
+ test_log10.recover
+ recover-missing-dbfile.abortrecover
+ recover-missing-dbfile-2.abortrecover
+ loader-tpch-load.loader
+ )
+
+ ## #5138 only reproduces when using the static library.
+ list(REMOVE_ITEM tdb_bins test-5138.tdb)
+ add_executable(test-5138.tdb test-5138)
+ target_link_libraries(test-5138.tdb ${LIBTOKUDB}_static z ${LIBTOKUPORTABILITY}_static ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_SYSTEM_LIBS})
+ add_space_separated_property(TARGET test-5138.tdb COMPILE_FLAGS -fvisibility=hidden)
+ add_ydb_test(test-5138.tdb)
+
+ foreach(bin ${tdb_bins})
+ get_filename_component(base ${bin} NAME_WE)
+
+ add_executable(${base}.tdb ${base})
+ # Some of the symbols in util may not be exported properly by
+ # libtokudb.so.
+ # We link the test with util directly so that the test code itself can use
+ # some of those things (i.e. kibbutz in the threaded tests).
+ target_link_libraries(${base}.tdb util ${LIBTOKUDB} ${LIBTOKUPORTABILITY})
+ add_space_separated_property(TARGET ${base}.tdb COMPILE_FLAGS -fvisibility=hidden)
+ endforeach(bin)
+
+ foreach(bin loader-cleanup-test.tdb diskfull.tdb)
+ set_property(TARGET ${bin} APPEND PROPERTY
+ COMPILE_DEFINITIONS DONT_DEPRECATE_WRITES)
+ endforeach(bin)
+
+ macro(declare_custom_tests)
+ foreach(test ${ARGN})
+ list(REMOVE_ITEM tdb_tests ${test})
+ endforeach(test)
+ endmacro(declare_custom_tests)
+
+ declare_custom_tests(test1426.tdb)
+
+ string(REGEX REPLACE "\\.cc(;|$)" ".tdb\\1" recover_would_be_tdb_tests "${recover_srcs}")
+ declare_custom_tests(${recover_would_be_tdb_tests})
+
+ declare_custom_tests(powerfail.tdb)
+ add_test(ydb/powerfail.tdb echo must run powerfail by hand)
+
+ declare_custom_tests(checkpoint_stress.tdb)
+ configure_file(run_checkpoint_stress_test.sh . COPYONLY)
+ add_test(NAME ydb/checkpoint_stress.tdb
+ COMMAND run_checkpoint_stress_test.sh $<TARGET_FILE:checkpoint_stress.tdb> 5 5001 137)
+ setup_toku_test_properties(ydb/checkpoint_stress.tdb checkpoint_stress.tdb)
+
+ configure_file(run_recover_stress_test.sh . COPYONLY)
+ add_test(NAME ydb/recover_stress.tdb
+ COMMAND run_recover_stress_test.sh $<TARGET_FILE:checkpoint_stress.tdb> 5 5001 137)
+ setup_toku_test_properties(ydb/recover_stress.tdb recover_stress.tdb)
+
+ declare_custom_tests(diskfull.tdb)
+ configure_file(run_diskfull_test.sh . COPYONLY)
+ add_test(NAME ydb/diskfull.tdb
+ COMMAND run_diskfull_test.sh $<TARGET_FILE:diskfull.tdb> 134)
+ setup_toku_test_properties(ydb/diskfull.tdb diskfull.tdb)
+
+ declare_custom_tests(recovery_fileops_unit.tdb)
+ configure_file(run_recovery_fileops_unit.sh . COPYONLY)
+ file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/recovery_fileops_unit_dir")
+ foreach(ov c d r)
+
+ if (ov STREQUAL c)
+ set(gset 0)
+ set(hset 0)
+ else ()
+ set(gset 0 1 2 3 4 5)
+ set(hset 0 1)
+ endif ()
+
+ foreach(av 0 1)
+ foreach(bv 0 1)
+
+ if (bv)
+ set(dset 0 1)
+ set(eset 0 1)
+ else ()
+ set(dset 0)
+ set(eset 0)
+ endif ()
+
+ foreach(cv 0 1 2)
+ foreach(dv ${dset})
+ foreach(ev ${eset})
+ foreach(fv 0 1)
+ foreach(gv ${gset})
+ foreach(hv ${hset})
+
+ if ((NOT ov STREQUAL c) AND (NOT cv) AND ((NOT bv) OR (NOT ev) OR (dv)))
+ set(iset 0 1)
+ else ()
+ set(iset 0)
+ endif ()
+
+ foreach(iv ${iset})
+ set(testname "ydb/recovery_fileops_unit.${ov}${av}${bv}${cv}${dv}${ev}${fv}${gv}${hv}${iv}")
+ set(envdir "recovery_fileops_unit_dir/${ov}${av}${bv}${cv}${dv}${ev}${fv}${gv}${hv}${iv}")
+ set(errfile "recovery_fileops_unit_dir/${ov}${av}${bv}${cv}${dv}${ev}${fv}${gv}${hv}${iv}.ctest-errors")
+ add_test(NAME ${testname}
+ COMMAND run_recovery_fileops_unit.sh $<TARGET_FILE:recovery_fileops_unit.tdb> ${errfile} 137
+ -O ${ov} -A ${av} -B ${bv} -C ${cv} -D ${dv} -E ${ev} -F ${fv} -G ${gv} -H ${hv} -I ${iv}
+ )
+ setup_toku_test_properties(${testname} ${envdir})
+ set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${errfile}")
+ endforeach(iv)
+ endforeach(hv)
+ endforeach(gv)
+ endforeach(fv)
+ endforeach(ev)
+ endforeach(dv)
+ endforeach(cv)
+ endforeach(bv)
+ endforeach(av)
+ endforeach(ov)
+
+ if (NOT (CMAKE_SYSTEM_NAME MATCHES Darwin OR USE_GCOV))
+ declare_custom_tests(helgrind1.tdb)
+ add_test(NAME ydb/helgrind_helgrind1.tdb
+ COMMAND valgrind --quiet --tool=helgrind --error-exitcode=1 --log-file=helgrind1.tdb.deleteme $<TARGET_FILE:helgrind1.tdb>)
+ setup_toku_test_properties(ydb/helgrind_helgrind1.tdb helgrind_helgrind1.tdb)
+ set_tests_properties(ydb/helgrind_helgrind1.tdb PROPERTIES WILL_FAIL TRUE)
+ endif()
+ declare_custom_tests(helgrind2.tdb)
+ declare_custom_tests(helgrind3.tdb)
+ add_ydb_helgrind_test(helgrind2.tdb)
+ add_ydb_helgrind_test(helgrind3.tdb)
+
+ declare_custom_tests(test_groupcommit_count.tdb)
+ add_ydb_test(test_groupcommit_count.tdb -n 1)
+ add_ydb_drd_test(test_groupcommit_count.tdb -n 2)
+
+ add_ydb_drd_test(test_4015.tdb)
+
+ # We link the locktree so that stress test 0 can call some
+ # functions (ie: lock escalation) directly.
+ target_link_libraries(test_stress0.tdb locktree)
+
+ # Set up default stress tests and drd tests. Exclude hot_index.
+ foreach(src ${stress_test_srcs})
+ if(NOT ${src} MATCHES hot_index)
+ get_filename_component(base ${src} NAME_WE)
+ set(test ${base}.tdb)
+
+ if (${src} MATCHES test_stress0)
+ add_ydb_test(${test} --num_elements 512 --num_seconds 1000 --join_timeout 600)
+ else ()
+ add_ydb_test(${test} --num_elements 150000 --num_seconds 1000 --join_timeout 600)
+ endif ()
+
+ add_ydb_drd_test_aux(drd_tiny_${test} ${test} --num_seconds 5 --num_elements 150 --join_timeout 3000)
+ set_tests_properties(ydb/drd_tiny_${test} PROPERTIES TIMEOUT 3600)
+
+ add_test(ydb/drd_mid_${test}/prepare ${test} --only_create --num_elements 10000)
+ setup_toku_test_properties(ydb/drd_mid_${test}/prepare drd_mid_${test})
+ add_ydb_drd_test_aux(drd_mid_${test} ${test} --only_stress --num_elements 10000 --num_seconds 100 --join_timeout 14400)
+ set_tests_properties(ydb/drd_mid_${test} PROPERTIES
+ DEPENDS ydb/drd_mid_${test}/prepare
+ REQUIRED_FILES "drd_mid_${test}.ctest-data"
+ TIMEOUT 15000
+ )
+
+ add_test(ydb/drd_large_${test}/prepare ${test} --only_create --num_elements 150000)
+ setup_toku_test_properties(ydb/drd_large_${test}/prepare drd_large_${test})
+ add_ydb_drd_test_aux(drd_large_${test} ${test} --only_stress --num_elements 150000 --num_seconds 1000 --join_timeout 28800)
+ set_tests_properties(ydb/drd_large_${test} PROPERTIES
+ DEPENDS ydb/drd_large_${test}/prepare
+ REQUIRED_FILES "drd_large_${test}.ctest-data"
+ TIMEOUT 30000
+ )
+ endif()
+ endforeach(src)
+
+ # Set up upgrade tests. Exclude test_stress_openclose
+ foreach(src ${stress_test_srcs})
+ if (NOT ${src} MATCHES test_stress_openclose)
+ get_filename_component(base ${src} NAME_WE)
+ set(test ${base}.tdb)
+
+ foreach(oldver 4.2.0 5.0.8 5.2.7 6.0.0 6.1.0 6.5.1 6.6.3)
+ set(versiondir ${TOKUDB_DATA}/old-stress-test-envs/${oldver})
+ if (NOT EXISTS "${versiondir}/" AND NOT WARNED_ABOUT_DATA AND CMAKE_PROJECT_NAME STREQUAL TokuDB)
+ message(WARNING "Test data for upgrade tests for version ${oldver} doesn't exist, check out ${versiondir}/*-2000-dir first or upgrade stress tests may fail.")
+ set(WARNED_ABOUT_DATA 1)
+ endif ()
+ foreach(p_or_s pristine stressed)
+ if (NOT (${base} MATCHES test_stress4 AND ${p_or_s} MATCHES stressed))
+ foreach(size 2000)
+ set(oldenvdir "${versiondir}/saved${p_or_s}-${size}-dir")
+ set(envdirbase "${upgrade}_${oldver}_${p_or_s}_${size}_${test}")
+ set(envdir "${envdirbase}.ctest-data")
+ set(testnamebase ydb/${test}/upgrade/${oldver}/${p_or_s}/${size})
+
+ add_test(NAME ${testnamebase}/remove
+ COMMAND ${CMAKE_COMMAND} -E remove_directory "${envdir}")
+ add_test(NAME ${testnamebase}/copy
+ COMMAND ${CMAKE_COMMAND} -E copy_directory "${oldenvdir}" "${envdir}")
+ set_tests_properties(${testnamebase}/copy PROPERTIES
+ DEPENDS ${testnamebase}/remove
+ REQUIRED_FILES "${oldenvdir}")
+
+ add_test(NAME ${testnamebase}
+ COMMAND ${test} --only_stress --num_elements ${size} --num_seconds 600 --join_timeout 7200)
+ setup_toku_test_properties(${testnamebase} "${envdirbase}")
+ set_tests_properties(${testnamebase} PROPERTIES
+ DEPENDS ${testnamebase}/copy
+ REQUIRED_FILES "${envdir}"
+ TIMEOUT 10800)
+ endforeach(size)
+ endif ()
+ endforeach(p_or_s)
+ endforeach(oldver)
+ endif ()
+ endforeach(src)
+
+ if (NOT EXISTS "${TOKUDB_DATA}/test_5902/" AND NOT WARNED_ABOUT_DATA AND CMAKE_PROJECT_NAME STREQUAL TokuDB)
+ message(WARNING "Test data for dump-env.tdb doesn't exist, check out ${TOKUDB_DATA}/test_5902 first or dump-env.tdb may fail.")
+ set(WARNED_ABOUT_DATA 1)
+ endif ()
+ declare_custom_tests(dump-env.tdb)
+ add_test(NAME ydb/dump-env.tdb/remove
+ COMMAND ${CMAKE_COMMAND} -E remove_directory "dump-env.tdb.ctest-data")
+ add_test(NAME ydb/dump-env.tdb/copy
+ COMMAND ${CMAKE_COMMAND} -E copy_directory "${TOKUDB_DATA}/test_5902" "dump-env.tdb.ctest-data")
+ set_tests_properties(ydb/dump-env.tdb/copy PROPERTIES
+ DEPENDS ydb/dump-env.tdb/remove
+ REQUIRED_FILES "${TOKUDB_DATA}/test_5902")
+ add_ydb_test(dump-env.tdb)
+ set_tests_properties(ydb/dump-env.tdb PROPERTIES
+ DEPENDS ydb/dump-env.tdb/copy
+ REQUIRED_FILES "dump-env.tdb.ctest-data")
+
+ ## for some reason this rule doesn't run with the makefile and it crashes with this rule, so I'm disabling this special case
+ #declare_custom_tests(test_thread_stack.tdb)
+ #add_custom_command(OUTPUT run_test_thread_stack.sh
+ # COMMAND install "${CMAKE_CURRENT_SOURCE_DIR}/run_test_thread_stack.sh" "${CMAKE_CFG_INTDIR}"
+ # MAIN_DEPENDENCY run_test_thread_stack.sh
+ # VERBATIM)
+ #add_custom_target(install_run_test_thread_stack.sh ALL DEPENDS run_test_thread_stack.sh)
+ #add_test(ydb/test_thread_stack.tdb run_test_thread_stack.sh "${CMAKE_CFG_INTDIR}/test_thread_stack.tdb")
+
+ declare_custom_tests(root_fifo_41.tdb)
+ foreach(num RANGE 1 100)
+ add_ydb_test_aux(root_fifo_41_${num}_populate.tdb root_fifo_41.tdb -n ${num} -populate)
+ add_ydb_test_aux(root_fifo_41_${num}_nopopulate.tdb root_fifo_41.tdb -n ${num})
+ endforeach(num)
+
+ add_ydb_test_aux(test3039_small.tdb test3039.tdb -n 1000)
+
+ declare_custom_tests(test_abort4.tdb)
+ foreach(num RANGE -1 19)
+ add_ydb_test_aux(test_abort4_${num}_0.tdb test_abort4.tdb -c 0 -l ${num})
+ add_ydb_test_aux(test_abort4_${num}_1.tdb test_abort4.tdb -c 1 -l ${num})
+ endforeach(num)
+
+ set(old_loader_upgrade_data "${TOKUDB_DATA}/env_preload.4.2.0.emptydictionaries.cleanshutdown")
+ if (NOT EXISTS "${old_loader_upgrade_data}/" AND NOT WARNED_ABOUT_DATA AND CMAKE_PROJECT_NAME STREQUAL TokuDB)
+ message(WARNING "Test data for loader upgrade tests doesn't exist, check out ${old_loader_upgrade_data} first, or loader-stress-test3.tdb may fail.")
+ set(WARNED_ABOUT_DATA 1)
+ endif ()
+ function(add_loader_upgrade_test name bin)
+ add_test(NAME ydb/${name}/remove
+ COMMAND ${CMAKE_COMMAND} -E remove_directory "${name}.ctest-data")
+ add_test(NAME ydb/${name}/copy
+ COMMAND ${CMAKE_COMMAND} -E copy_directory "${old_loader_upgrade_data}" "${name}.ctest-data")
+ set_tests_properties(ydb/${name}/copy PROPERTIES
+ DEPENDS ydb/${name}/remove
+ REQUIRED_FILES "${old_loader_upgrade_data}")
+ add_ydb_test_aux(${name} ${bin} -u ${ARGN})
+ set_tests_properties(ydb/${name} PROPERTIES
+ DEPENDS ydb/${name}/copy
+ REQUIRED_FILES "${name}.ctest-data")
+ endfunction(add_loader_upgrade_test)
+
+ list(REMOVE_ITEM loader_tests loader-stress-test.loader)
+ add_ydb_test_aux(loader-stress-test0.tdb loader-stress-test.tdb -c)
+ add_ydb_test_aux(loader-stress-test1.tdb loader-stress-test.tdb -c -p)
+ add_ydb_test_aux(loader-stress-test2.tdb loader-stress-test.tdb -r 5000 -s)
+ add_loader_upgrade_test(loader-stress-test3.tdb loader-stress-test.tdb -c)
+ add_ydb_test_aux(loader-stress-test4.tdb loader-stress-test.tdb -r 10000000 -c)
+ add_ydb_test_aux(loader-stress-test0z.tdb loader-stress-test.tdb -c -z)
+ add_ydb_test_aux(loader-stress-test1z.tdb loader-stress-test.tdb -c -p -z)
+ add_ydb_test_aux(loader-stress-test2z.tdb loader-stress-test.tdb -r 5000 -s -z)
+ add_loader_upgrade_test(loader-stress-test3z.tdb loader-stress-test.tdb -c -z)
+ add_ydb_test_aux(loader-stress-test4z.tdb loader-stress-test.tdb -r 500000 -c -z --valsize 28)
+
+ list(REMOVE_ITEM loader_tests loader-dup-test.loader)
+ add_ydb_test_aux(loader-dup-test0.tdb loader-dup-test.tdb)
+ add_ydb_test_aux(loader-dup-test1.tdb loader-dup-test.tdb -d 1 -r 500000)
+ add_ydb_test_aux(loader-dup-test2.tdb loader-dup-test.tdb -d 1 -r 1000000)
+ add_ydb_test_aux(loader-dup-test3.tdb loader-dup-test.tdb -d 1 -s -r 100)
+ add_ydb_test_aux(loader-dup-test4.tdb loader-dup-test.tdb -d 1 -s -r 1000)
+ add_ydb_test_aux(loader-dup-test5.tdb loader-dup-test.tdb -d 1 -s -r 1000 -E)
+ add_ydb_test_aux(loader-dup-test0z.tdb loader-dup-test.tdb -z)
+ add_ydb_test_aux(loader-dup-test1z.tdb loader-dup-test.tdb -d 1 -r 500000 -z)
+ add_ydb_test_aux(loader-dup-test2z.tdb loader-dup-test.tdb -d 1 -r 1000000 -z)
+ add_ydb_test_aux(loader-dup-test3z.tdb loader-dup-test.tdb -d 1 -s -r 100 -z)
+ add_ydb_test_aux(loader-dup-test4z.tdb loader-dup-test.tdb -d 1 -s -r 1000 -z)
+ add_ydb_test_aux(loader-dup-test5z.tdb loader-dup-test.tdb -d 1 -s -r 1000 -E -z)
+
+ ## as part of #4503, we took out test 1 and 3
+ list(REMOVE_ITEM loader_tests loader-cleanup-test.loader)
+ add_ydb_test_aux(loader-cleanup-test0.tdb loader-cleanup-test.tdb -s -r 800)
+ #add_ydb_test_aux(loader-cleanup-test1.tdb loader-cleanup-test.tdb -s -r 800 -p)
+ add_ydb_test_aux(loader-cleanup-test2.tdb loader-cleanup-test.tdb -s -r 8000)
+ #add_ydb_test_aux(loader-cleanup-test3.tdb loader-cleanup-test.tdb -s -r 8000 -p)
+ add_ydb_test_aux(loader-cleanup-test0z.tdb loader-cleanup-test.tdb -s -r 800 -z)
+ add_ydb_test_aux(loader-cleanup-test2z.tdb loader-cleanup-test.tdb -s -r 8000 -z)
+
+ declare_custom_tests(keyrange.tdb)
+ add_ydb_test_aux(keyrange-get0.tdb keyrange.tdb --get 0)
+ add_ydb_test_aux(keyrange-get1.tdb keyrange.tdb --get 1)
+ add_ydb_test_aux(keyrange-random-get0.tdb keyrange.tdb --get 0 --random_keys 1)
+ add_ydb_test_aux(keyrange-random-get1.tdb keyrange.tdb --get 1 --random_keys 1)
+ add_ydb_test_aux(keyrange-loader-get0.tdb keyrange.tdb --get 0 --loader 1)
+ add_ydb_test_aux(keyrange-loader-get1.tdb keyrange.tdb --get 1 --loader 1)
+
+ declare_custom_tests(maxsize-for-loader.tdb)
+ add_ydb_test_aux(maxsize-for-loader-A.tdb maxsize-for-loader.tdb -f -c)
+ add_ydb_test_aux(maxsize-for-loader-B.tdb maxsize-for-loader.tdb -c)
+ add_ydb_test_aux(maxsize-for-loader-Az.tdb maxsize-for-loader.tdb -f -z -c)
+ add_ydb_test_aux(maxsize-for-loader-Bz.tdb maxsize-for-loader.tdb -z -c)
+
+ declare_custom_tests(hotindexer-undo-do-test.tdb)
+ file(GLOB hotindexer_tests RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "hotindexer-undo-do-tests/*.test")
+ file(GLOB hotindexer_results RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "hotindexer-undo-do-tests/*.result")
+ configure_file(run-hotindexer-undo-do-tests.bash . COPYONLY)
+ foreach(result ${hotindexer_results})
+ configure_file(${result} ${result} COPYONLY)
+ endforeach(result)
+ foreach(test ${hotindexer_tests})
+ configure_file(${test} ${test} COPYONLY)
+ add_test(NAME ydb/${test} COMMAND run-hotindexer-undo-do-tests.bash ${test})
+ setup_toku_test_properties(ydb/${test} ${test})
+ endforeach()
+
+ foreach(test ${tdb_tests})
+ add_ydb_test(${test})
+ endforeach(test)
+
+ configure_file(run_recover_test.sh . COPYONLY)
+ foreach(recover_test ${recover_tests})
+ get_filename_component(base ${recover_test} NAME_WE)
+ add_test(NAME ydb/${recover_test}
+ COMMAND run_recover_test.sh $<TARGET_FILE:${base}.tdb> "${recover_test}.ctest-data" $<TARGET_FILE:tdb-recover> $<TARGET_FILE:tokudb_dump>)
+ setup_toku_test_properties(ydb/${recover_test} ${recover_test})
+ endforeach(recover_test)
+
+ configure_file(run_abortrecover_test.sh . COPYONLY)
+ foreach(abortrecover_test ${abortrecover_tests})
+ get_filename_component(base ${abortrecover_test} NAME_WE)
+ add_test(NAME ydb/${abortrecover_test}
+ COMMAND run_abortrecover_test.sh $<TARGET_FILE:${base}.tdb>)
+ setup_toku_test_properties(ydb/${abortrecover_test} ${abortrecover_test})
+ endforeach(abortrecover_test)
+ ## alternate implementation, doesn't work because the abort phase crashes and we can't tell cmake that's expected
+ # foreach(abortrecover_test ${abortrecover_tests})
+ # get_filename_component(base ${abortrecover_test} NAME_WE)
+ # set(test ${base}.tdb)
+ # add_test(NAME ydb/${test}/abort
+ # COMMAND ${test} --test)
+ # setup_toku_test_properties(ydb/${test}/abort ${abortrecover_test})
+ # set_tests_properties(ydb/${test}/abort PROPERTIES WILL_FAIL TRUE)
+
+ # add_test(NAME ydb/${test}/recover
+ # COMMAND ${test} --recover)
+ # setup_toku_test_properties(ydb/${test}/recover ${abortrecover_test})
+ # set_tests_properties(ydb/${test}/recover PROPERTIES
+ # DEPENDS ydb/${test}/abort
+ # REQUIRED_FILES "${abortrecover_test}.ctest-data")
+ # endforeach(abortrecover_test)
+
+ foreach(loader_test ${loader_tests})
+ get_filename_component(base ${loader_test} NAME_WE)
+ add_ydb_test_aux(${base}.nop.loader ${base}.tdb)
+ add_ydb_test_aux(${base}.p.loader ${base}.tdb -p)
+ add_ydb_test_aux(${base}.comp.loader ${base}.tdb -z)
+ if("${tdb_tests_that_should_fail}" MATCHES "${base}.loader")
+ list(REMOVE_ITEM tdb_tests_that_should_fail ${base}.loader)
+ list(APPEND tdb_tests_that_should_fail ${base}.nop.loader ${base}.p.loader ${base}.comp.loader)
+ endif()
+ endforeach(loader_test)
+
+ set(tdb_tests_that_should_fail "ydb/${tdb_tests_that_should_fail}")
+ string(REGEX REPLACE ";" ";ydb/" tdb_tests_that_should_fail "${tdb_tests_that_should_fail}")
+ set_tests_properties(${tdb_tests_that_should_fail} PROPERTIES WILL_FAIL TRUE)
+
+ ## give some tests, that time out normally, 1 hour to complete
+ set(long_tests
+ ydb/drd_test_groupcommit_count.tdb
+ ydb/env-put-multiple.tdb
+ ydb/filesize.tdb
+ ydb/loader-cleanup-test0.tdb
+ ydb/loader-cleanup-test0z.tdb
+ ydb/manyfiles.tdb
+ ydb/recover-loader-test.abortrecover
+ ydb/recovery_fileops_stress.tdb
+ ydb/root_fifo_1.tdb
+ ydb/root_fifo_2.tdb
+ ydb/root_fifo_31.tdb
+ ydb/root_fifo_32.tdb
+ ydb/shutdown-3344.tdb
+ ydb/stat64-create-modify-times.tdb
+ ydb/test1572.tdb
+ ydb/test_abort4_19_0.tdb
+ ydb/test_abort4_19_1.tdb
+ ydb/test_abort5.tdb
+ ydb/test_archive1.tdb
+ ydb/test_logmax.tdb
+ ydb/test_query.tdb
+ ydb/test_txn_abort5.tdb
+ ydb/test_txn_abort5a.tdb
+ ydb/test_txn_abort6.tdb
+ ydb/test_txn_nested2.tdb
+ ydb/test_txn_nested4.tdb
+ ydb/test_txn_nested5.tdb
+ ydb/test_update_broadcast_stress.tdb
+ )
+ set_tests_properties(${long_tests} PROPERTIES TIMEOUT 3600)
+ ## some take even longer, with valgrind
+ set(extra_long_tests
+ ydb/drd_test_4015.tdb
+ ydb/hotindexer-with-queries.tdb
+ ydb/hot-optimize-table-tests.tdb
+ ydb/loader-cleanup-test2.tdb
+ ydb/loader-cleanup-test2z.tdb
+ ydb/loader-dup-test0.tdb
+ ydb/loader-stress-del.nop.loader
+ ydb/loader-stress-del.p.loader
+ ydb/loader-stress-del.comp.loader
+ ydb/test3039.tdb
+ ydb/test_update_stress.tdb
+ )
+ set_tests_properties(${extra_long_tests} PROPERTIES TIMEOUT 7200)
+ ## these really take a long time with valgrind
+ set(phenomenally_long_tests
+ ydb/checkpoint_stress.tdb
+ ydb/loader-stress-test4.tdb
+ ydb/loader-stress-test4z.tdb
+ ydb/recover_stress.tdb
+ ydb/test3529.tdb
+ ydb/test_insert_unique.tdb
+ )
+ set_tests_properties(${phenomenally_long_tests} PROPERTIES TIMEOUT 14400)
+endif(BUILD_TESTING OR BUILD_SRC_TESTS)
diff --git a/storage/tokudb/PerconaFT/src/tests/big-nested-abort-abort.cc b/storage/tokudb/PerconaFT/src/tests/big-nested-abort-abort.cc
new file mode 100644
index 00000000000..796ce7dd04c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/big-nested-abort-abort.cc
@@ -0,0 +1,151 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test to see if a big nested transaction (so big that it's rollbacks spill into a file)
+ * can commit properly.
+ * Four Tests:
+ * big child aborts, parent aborts (This test)
+ * big child aborts, parent commits
+ * big child commits, parent aborts
+ * big child commits, parent commits
+ */
+
+#include <db.h>
+#include <sys/stat.h>
+
+int N = 50000;
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *xchild, *xparent;
+
+static void insert (int i, int j) {
+ char hello[30], there[30];
+ DBT key,data;
+ if (verbose) printf("Insert %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", j);
+ int r = db->put(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void lookup (int i, int expect, int expectj) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ memset(&data, 0, sizeof(data));
+ if (verbose) printf("Looking up %d (expecting %s)\n", i, expect==0 ? "to find" : "not to find");
+ int r = db->get(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &data,
+ 0);
+ assert(expect==r);
+ if (expect==0) {
+ CKERR(r);
+ snprintf(there, sizeof(there), "there%d", expectj);
+ assert(data.size==strlen(there)+1);
+ assert(strcmp((char*)data.data, there)==0);
+ }
+}
+
+static void
+test_abort_abort (void) {
+ int i, r;
+ assert(N%2==0); // this test won't work if N is too small
+ r=env->txn_begin(env, 0, &xchild, 0); CKERR(r);
+ for (i=0; i<N/2; i++) {
+ insert(i*2,i*4+1);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &xparent, 0); CKERR(r);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ insert(i, i);
+ }
+ r=xchild->abort(xchild); CKERR(r);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, (i%2==0)?0:DB_NOTFOUND, i*2+1);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ r=xparent->abort(xparent); CKERR(r);
+ r=env->txn_begin(env, 0, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, (i%2==0)?0:DB_NOTFOUND, i*2+1);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+}
+
+static void
+setup (void) {
+ DB_TXN *txn;
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+
+ r=env->set_redzone(env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int r;
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ test_abort_abort();
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/big-nested-abort-commit.cc b/storage/tokudb/PerconaFT/src/tests/big-nested-abort-commit.cc
new file mode 100644
index 00000000000..c3ebb27275a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/big-nested-abort-commit.cc
@@ -0,0 +1,149 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test to see if a big nested transaction (so big that it's rollbacks spill into a file)
+ * can commit properly.
+ * Four Tests:
+ * big child aborts, parent aborts
+ * big child aborts, parent commits (This test)
+ * big child commits, parent aborts
+ * big child commits, parent commits
+ */
+
+#include <db.h>
+#include <sys/stat.h>
+
+int N = 50000;
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *xchild, *xparent;
+
+static void insert (int i, int j) {
+ char hello[30], there[30];
+ DBT key,data;
+ if (verbose) printf("Insert %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", j);
+ int r = db->put(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void lookup (int i, int expect, int expectj) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ memset(&data, 0, sizeof(data));
+ if (verbose) printf("Looking up %d (expecting %s)\n", i, expect==0 ? "to find" : "not to find");
+ int r = db->get(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &data,
+ 0);
+ assert(expect==r);
+ if (expect==0) {
+ CKERR(r);
+ snprintf(there, sizeof(there), "there%d", expectj);
+ assert(data.size==strlen(there)+1);
+ assert(strcmp((char*)data.data, there)==0);
+ }
+}
+
+static void
+test_abort_commit (void) {
+ int i, r;
+ r=env->txn_begin(env, 0, &xchild, 0); CKERR(r);
+ for (i=0; i<N/2; i++) {
+ insert(i*2,i*4+1);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &xparent, 0); CKERR(r);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ insert(i, i);
+ }
+ r=xchild->abort(xchild); CKERR(r);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, (i%2==0)?0:DB_NOTFOUND, i*2+1);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ r=xparent->commit(xparent, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, (i%2==0)?0:DB_NOTFOUND, i*2+1);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+}
+
+static void
+setup (void) {
+ DB_TXN *txn;
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int r;
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ test_abort_commit();
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/big-nested-commit-abort.cc b/storage/tokudb/PerconaFT/src/tests/big-nested-commit-abort.cc
new file mode 100644
index 00000000000..55ac00f19c4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/big-nested-commit-abort.cc
@@ -0,0 +1,144 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test to see if a big nested transaction (so big that it's rollbacks spill into a file)
+ * can commit properly.
+ * Four Tests:
+ * big child aborts, parent aborts
+ * big child aborts, parent commits
+ * big child commits, parent aborts (This test)
+ * big child commits, parent commits
+ */
+
+#include <db.h>
+#include <sys/stat.h>
+
+int N = 50000;
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *xchild, *xparent;
+
+static void insert (int i) {
+ char hello[30], there[30];
+ DBT key,data;
+ if (verbose) printf("Insert %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ int r = db->put(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void lookup (int i, int expect, int expectj) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ memset(&data, 0, sizeof(data));
+ if (verbose) printf("Looking up %d (expecting %s)\n", i, expect==0 ? "to find" : "not to find");
+ int r = db->get(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &data,
+ 0);
+ assert(expect==r);
+ if (expect==0) {
+ CKERR(r);
+ snprintf(there, sizeof(there), "there%d", expectj);
+ assert(data.size==strlen(there)+1);
+ assert(strcmp((char*)data.data, there)==0);
+ }
+}
+
+static void
+test_commit_abort (void) {
+ int i, r;
+ r=env->txn_begin(env, 0, &xparent, 0); CKERR(r);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ insert(i);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, 0, i);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ r=xparent->abort(xparent); CKERR(r);
+ r=env->txn_begin(env, 0, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, DB_NOTFOUND, 0);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+}
+
+static void
+setup (void) {
+ DB_TXN *txn;
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int r;
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ test_commit_abort();
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/big-nested-commit-commit.cc b/storage/tokudb/PerconaFT/src/tests/big-nested-commit-commit.cc
new file mode 100644
index 00000000000..414ad3ef180
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/big-nested-commit-commit.cc
@@ -0,0 +1,145 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test to see if a big nested transaction (so big that it's rollbacks spill into a file)
+ * can commit properly.
+ * Four Tests:
+ * big child aborts, parent aborts
+ * big child aborts, parent commits
+ * big child commits, parent aborts
+ * big child commits, parent commits (This test)
+ */
+
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *xchild, *xparent;
+
+static void insert (int i) {
+ char hello[30], there[30];
+ DBT key,data;
+ if (verbose) printf("Insert %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ int r = db->put(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void lookup (int i, int expect, int expectj) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ memset(&data, 0, sizeof(data));
+ if (verbose) printf("Looking up %d (expecting %s)\n", i, expect==0 ? "to find" : "not to find");
+ int r = db->get(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &data,
+ 0);
+ assert(expect==r);
+ if (expect==0) {
+ CKERR(r);
+ snprintf(there, sizeof(there), "there%d", expectj);
+ assert(data.size==strlen(there)+1);
+ assert(strcmp((char*)data.data, there)==0);
+ }
+}
+
+int N = 50000;
+
+static void
+test_commit_commit (void) {
+ int i, r;
+ r=env->txn_begin(env, 0, &xparent, 0); CKERR(r);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ insert(i);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, 0, i);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ r=xparent->commit(xparent, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, 0, i);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+}
+
+static void
+setup (void) {
+ DB_TXN *txn;
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ r=env->set_redzone(env, 0); CKERR(r);
+
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int r;
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ test_commit_commit();
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/big-shutdown.cc b/storage/tokudb/PerconaFT/src/tests/big-shutdown.cc
new file mode 100644
index 00000000000..9fb84816557
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/big-shutdown.cc
@@ -0,0 +1,136 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Create a lot of dirty nodes, kick off a checkpoint, and close the environment.
+// Measure the time it takes to close the environment since we are speeding up that
+// function.
+
+#include "test.h"
+#include <toku_time.h>
+
+// Insert max_rows key/val pairs into the db
+static void do_inserts(DB_ENV *env, DB *db, uint64_t max_rows, size_t val_size) {
+ char val_data[val_size]; memset(val_data, 0, val_size);
+ int r;
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ for (uint64_t i = 1; i <= max_rows; i++) {
+ // pick a sequential key but it does not matter for this test.
+ uint64_t k[2] = {
+ htonl(i), random64(),
+ };
+ DBT key = { .data = k, .size = sizeof k };
+ DBT val = { .data = val_data, .size = (uint32_t) val_size };
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+
+ if ((i % 1000) == 0) {
+ if (verbose)
+ fprintf(stderr, "put %" PRIu64 "\n", i);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+ }
+ }
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+}
+
+// Create a cache with a lot of dirty nodes, kick off a checkpoint, and measure the time to
+// close the environment.
+static void big_shutdown(void) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_cachesize(env, 8, 0, 1);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ do_inserts(env, db, 1000000, 1024);
+
+ // kick the checkpoint thread
+ if (verbose)
+ fprintf(stderr, "env->checkpointing_set_period\n");
+ r = env->checkpointing_set_period(env, 2);
+ CKERR(r);
+ sleep(3);
+
+ if (verbose)
+ fprintf(stderr, "db->close\n");
+ r = db->close(db, 0);
+ CKERR(r);
+
+ // measure the shutdown time
+ uint64_t tstart = toku_current_time_microsec();
+ if (verbose)
+ fprintf(stderr, "env->close\n");
+ r = env->close(env, 0);
+ CKERR(r);
+ uint64_t tend = toku_current_time_microsec();
+ if (verbose)
+ fprintf(stderr, "env->close complete %" PRIu64 " sec\n", (tend - tstart)/1000000);
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // run the test
+ big_shutdown();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/bigtxn27.cc b/storage/tokudb/PerconaFT/src/tests/bigtxn27.cc
new file mode 100644
index 00000000000..7a003fd2a3c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/bigtxn27.cc
@@ -0,0 +1,172 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <pthread.h>
+
+// verify that a commit of a big txn does not block the commits of other txn's
+// commit writer (0) happens before bigtxn commit (1) happens before checkpoint (2)
+static int test_state = 0;
+
+static void *checkpoint_thread(void *arg) {
+ sleep(1);
+ DB_ENV *env = (DB_ENV *) arg;
+ printf("%s start\n", __FUNCTION__);
+ int r = env->txn_checkpoint(env, 0, 0, 0);
+ assert(r == 0);
+ printf("%s done\n", __FUNCTION__);
+ int old_state = toku_sync_fetch_and_add(&test_state, 1);
+ assert(old_state == 2);
+ return arg;
+}
+
+struct writer_arg {
+ DB_ENV *env;
+ DB *db;
+ int k;
+};
+
+static void *w_thread(void *arg) {
+ sleep(2);
+ struct writer_arg *warg = (struct writer_arg *) arg;
+ DB_ENV *env = warg->env;
+ DB *db = warg->db;
+ int k = warg->k;
+ printf("%s start\n", __FUNCTION__);
+ int r;
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ assert(r == 0);
+ if (1) {
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &k, .size = sizeof k };
+ r = db->put(db, txn, &key, &val, 0);
+ assert(r == 0);
+ }
+ r = txn->commit(txn, 0);
+ assert(r == 0);
+ printf("%s done\n", __FUNCTION__);
+ int old_state = toku_sync_fetch_and_add(&test_state, 1);
+ assert(old_state == 0);
+ return arg;
+}
+
+static void bigtxn_progress(TOKU_TXN_PROGRESS progress, void *extra) {
+ printf("%s %" PRIu64 " %" PRIu64 " %p\n", __FUNCTION__, progress->entries_processed, progress->entries_total, extra);
+ sleep(1);
+}
+
+int test_main (int argc, char *const argv[]) {
+ int r;
+ int N = 25000;
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "--N") == 0 && i+1 < argc) {
+ N = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+
+ // avoid locktree escalation by picking a big enough lock tree
+ r = env->set_lk_max_memory(env, 128*1024*1024);
+ assert(r == 0);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+
+ r = db->open(db, NULL, "testit", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_TXN *bigtxn = NULL;
+ r = env->txn_begin(env, NULL, &bigtxn, 0);
+ assert(r == 0);
+
+ // use a big key so that the rollback log spills
+ char k[1024]; memset(k, 0, sizeof k);
+ char v[8]; memset(v, 0, sizeof v);
+
+ for (int i = 0; i < N; i++) {
+ memcpy(k, &i, sizeof i);
+ memcpy(v, &i, sizeof i);
+ DBT key = { .data = k, .size = sizeof k };
+ DBT val = { .data = v, .size = sizeof v };
+ r = db->put(db, bigtxn, &key, &val, 0);
+ assert(r == 0);
+ if ((i % 10000) == 0)
+ printf("put %d\n", i);
+ }
+
+ pthread_t checkpoint_tid = 0;
+ r = pthread_create(&checkpoint_tid, NULL, checkpoint_thread, env);
+ assert(r == 0);
+
+ pthread_t w_tid = 0;
+ struct writer_arg w_arg = { env, db, N };
+ r = pthread_create(&w_tid, NULL, w_thread, &w_arg);
+ assert(r == 0);
+
+ r = bigtxn->commit_with_progress(bigtxn, 0, bigtxn_progress, NULL);
+ assert(r == 0);
+ int old_state = toku_sync_fetch_and_add(&test_state, 1);
+ assert(old_state == 1);
+
+ void *ret;
+ r = pthread_join(w_tid, &ret);
+ assert(r == 0);
+ r = pthread_join(checkpoint_tid, &ret);
+ assert(r == 0);
+
+ r = db->close(db, 0);
+ assert(r == 0);
+
+ r = env->close(env, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blackhole.cc b/storage/tokudb/PerconaFT/src/tests/blackhole.cc
new file mode 100644
index 00000000000..1bb11af06fa
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blackhole.cc
@@ -0,0 +1,129 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that a db ignores insert messages in blackhole mode
+
+#include "test.h"
+#include <util/dbt.h>
+
+static DB *db;
+static DB *blackhole_db;
+static DB_ENV *env;
+
+static int num_inserts = 10000;
+
+static void fill_dbt(DBT *dbt, void *data, size_t size) {
+ dbt->data = data;
+ dbt->size = dbt->ulen = size;
+ dbt->flags = DB_DBT_USERMEM;
+}
+
+static void setup (bool use_txns) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, 0, 0);
+ int txnflags = use_txns ? (DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN) : 0;
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_PRIVATE|txnflags, 0777);
+
+ // create a regular db and a blackhole db
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db_create(&blackhole_db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "test.db", 0, DB_BTREE,
+ DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = blackhole_db->open(blackhole_db, NULL, "blackhole.db", 0, DB_BTREE,
+ DB_CREATE | DB_BLACKHOLE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+}
+
+static void cleanup (void) {
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = blackhole_db->close(blackhole_db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void test_blackhole(void) {
+ int r = 0;
+
+ for (int i = 0; i < num_inserts; i++) {
+ int k = random();
+ int v = k + 100;
+ DBT key, value;
+ fill_dbt(&key, &k, sizeof k);
+ fill_dbt(&value, &v, sizeof v);
+
+ // put a random key into the regular db.
+ r = db->put(db, NULL, &key, &value, 0);
+ assert(r == 0);
+
+ // put that key into the blackhole db.
+ r = blackhole_db->put(blackhole_db, NULL, &key, &value, 0);
+ assert(r == 0);
+
+ // we should be able to find this key in the regular db
+ int get_v;
+ DBT get_value;
+ fill_dbt(&get_value, &get_v, sizeof get_v);
+ r = db->get(db, NULL, &key, &get_value, 0);
+ assert(r == 0);
+ assert(*(int *)get_value.data == v);
+ assert(get_value.size == sizeof v);
+
+ // we shouldn't be able to get it back from the blackhole
+ r = blackhole_db->get(blackhole_db, NULL, &key, &get_value, 0);
+ assert(r == DB_NOTFOUND);
+ }
+}
+
+int test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ // without txns
+ setup(false);
+ test_blackhole();
+ cleanup();
+
+ // with txns
+ setup(true);
+ test_blackhole();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-first-empty.cc b/storage/tokudb/PerconaFT/src/tests/blocking-first-empty.cc
new file mode 100644
index 00000000000..d9d865254f1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-first-empty.cc
@@ -0,0 +1,181 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that cursor first on an empty tree with a write lock suspends the conflicting threads.
+
+#include "test.h"
+#include <toku_pthread.h>
+
+struct my_callback_context {
+ DBT key;
+ DBT val;
+};
+
+static int blocking_first_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ DBT const *found_key = a;
+ DBT const *found_val = b;
+ struct my_callback_context *context = (struct my_callback_context *) e;
+ copy_dbt(&context->key, found_key);
+ copy_dbt(&context->val, found_val);
+ return 0;
+}
+
+static void blocking_first(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on -inf +inf
+ r = cursor->c_getf_first(cursor, DB_RMW, blocking_first_callback, &context); assert(r == DB_NOTFOUND);
+
+ usleep(sleeptime);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+struct blocking_first_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_first_thread(void *arg) {
+ struct blocking_first_args *a = (struct blocking_first_args *) arg;
+ blocking_first(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
+ int r;
+ toku_pthread_t tids[nthreads];
+ struct blocking_first_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_first_thread, &a); assert(r == 0);
+ }
+ blocking_first(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 10;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ run_test(db_env, db, nthreads, nrows, sleeptime);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-first.cc b/storage/tokudb/PerconaFT/src/tests/blocking-first.cc
new file mode 100644
index 00000000000..9f3f8f4eb6a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-first.cc
@@ -0,0 +1,201 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that cursor first with a write lock suspends the conflicting threads.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+
+ uint64_t k = htonl(i);
+ uint64_t v = i;
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &v, .size = sizeof v };
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+}
+
+struct my_callback_context {
+ DBT key;
+ DBT val;
+};
+
+static int blocking_first_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ DBT const *found_key = a;
+ DBT const *found_val = b;
+ struct my_callback_context *context = (struct my_callback_context *) e;
+ copy_dbt(&context->key, found_key);
+ copy_dbt(&context->val, found_val);
+ return 0;
+}
+
+static void blocking_first(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on -inf ... 0
+ r = cursor->c_getf_first(cursor, DB_RMW, blocking_first_callback, &context); assert(r == 0);
+ usleep(sleeptime);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+struct blocking_first_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_first_thread(void *arg) {
+ struct blocking_first_args *a = (struct blocking_first_args *) arg;
+ blocking_first(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
+ int r;
+ toku_pthread_t tids[nthreads];
+ struct blocking_first_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_first_thread, &a); assert(r == 0);
+ }
+ blocking_first(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 10;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // populate the db
+ populate(db_env, db, nrows);
+
+ run_test(db_env, db, nthreads, nrows, sleeptime);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-last.cc b/storage/tokudb/PerconaFT/src/tests/blocking-last.cc
new file mode 100644
index 00000000000..a62d83eb46f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-last.cc
@@ -0,0 +1,201 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that cursor last operations with conflicting locks suspend the calling threads.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+
+ uint64_t k = htonl(i);
+ uint64_t v = i;
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &v, .size = sizeof v };
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+}
+
+struct my_callback_context {
+ DBT key;
+ DBT val;
+};
+
+static int blocking_last_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ DBT const *found_key = a;
+ DBT const *found_val = b;
+ struct my_callback_context *context = (struct my_callback_context *) e;
+ copy_dbt(&context->key, found_key);
+ copy_dbt(&context->val, found_val);
+ return 0;
+}
+
+static void blocking_last(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on -inf ... 0
+ r = cursor->c_getf_last(cursor, DB_RMW, blocking_last_callback, &context); assert(r == 0);
+ usleep(sleeptime);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+struct blocking_last_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_last_thread(void *arg) {
+ struct blocking_last_args *a = (struct blocking_last_args *) arg;
+ blocking_last(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
+ int r;
+ toku_pthread_t tids[nthreads];
+ struct blocking_last_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_last_thread, &a); assert(r == 0);
+ }
+ blocking_last(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 10;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // populate the db
+ populate(db_env, db, nrows);
+
+ run_test(db_env, db, nthreads, nrows, sleeptime);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-next-prev-deadlock.cc b/storage/tokudb/PerconaFT/src/tests/blocking-next-prev-deadlock.cc
new file mode 100644
index 00000000000..bd5697ba526
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-next-prev-deadlock.cc
@@ -0,0 +1,266 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that two transactions doing cursor next and prev operations detect a deadlock when
+// using write locking cursor operations.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static uint64_t get_key(DBT *key) {
+ uint64_t k = 0;
+ assert(key->size == sizeof k);
+ memcpy(&k, key->data, key->size);
+ return htonl(k);
+}
+
+static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+
+ uint64_t k = htonl(i);
+ uint64_t v = i;
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &v, .size = sizeof v };
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+}
+
+struct my_callback_context {
+ DBT key;
+ DBT val;
+};
+
+static int blocking_next_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ DBT const *found_key = a;
+ DBT const *found_val = b;
+ struct my_callback_context *context = (struct my_callback_context *) e;
+ copy_dbt(&context->key, found_key);
+ copy_dbt(&context->val, found_val);
+ return 0;
+}
+
+static void blocking_next(DB_ENV *db_env, DB *db, uint64_t nrows UU(), long sleeptime) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+
+ uint64_t i;
+ for (i = 0; ; i++) {
+ r = cursor->c_getf_next(cursor, DB_RMW, blocking_next_callback, &context);
+ if (r != 0)
+ break;
+ if (verbose)
+ printf("%lu next %" PRIu64 "\n", (unsigned long) toku_pthread_self(), get_key(&context.key));
+ usleep(sleeptime);
+ }
+
+ if (verbose)
+ printf("%lu next=%d\n", (unsigned long) toku_pthread_self(), r);
+ assert(r == DB_NOTFOUND || r == DB_LOCK_DEADLOCK);
+
+ int rr = cursor->c_close(cursor); assert(rr == 0);
+
+ if (r == DB_NOTFOUND) {
+ if (verbose) printf("%lu commit\n", (unsigned long) toku_pthread_self());
+ r = txn->commit(txn, 0);
+ } else {
+ if (verbose) printf("%lu abort\n", (unsigned long) toku_pthread_self());
+ r = txn->abort(txn);
+ }
+ assert(r == 0);
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+static void blocking_prev(DB_ENV *db_env, DB *db, uint64_t nrows UU(), long sleeptime) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+
+ uint64_t i;
+ for (i = 0; ; i++) {
+ r = cursor->c_getf_prev(cursor, DB_RMW, blocking_next_callback, &context);
+ if (r != 0)
+ break;
+ if (verbose)
+ printf("%lu prev %" PRIu64 "\n", (unsigned long) toku_pthread_self(), get_key(&context.key));
+ usleep(sleeptime);
+ }
+
+ if (verbose)
+ printf("%lu prev=%d\n", (unsigned long) toku_pthread_self(), r);
+ assert(r == DB_NOTFOUND || r == DB_LOCK_DEADLOCK);
+
+ int rr = cursor->c_close(cursor); assert(rr == 0);
+
+ if (r == DB_NOTFOUND) {
+ if (verbose) printf("%lu commit\n", (unsigned long) toku_pthread_self());
+ r = txn->commit(txn, 0);
+ } else {
+ if (verbose) printf("%lu abort\n", (unsigned long) toku_pthread_self());
+ r = txn->abort(txn);
+ }
+ assert(r == 0);
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+struct blocking_next_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_next_thread(void *arg) {
+ struct blocking_next_args *a = (struct blocking_next_args *) arg;
+ blocking_next(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
+ int r;
+ toku_pthread_t tids[nthreads];
+ struct blocking_next_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_next_thread, &a); assert(r == 0);
+ }
+ blocking_prev(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 10;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // populate the db
+ populate(db_env, db, nrows);
+
+ run_test(db_env, db, nthreads, nrows, sleeptime);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-next-prev.cc b/storage/tokudb/PerconaFT/src/tests/blocking-next-prev.cc
new file mode 100644
index 00000000000..729473bb7cf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-next-prev.cc
@@ -0,0 +1,272 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that two transactions doing cursor next and prev operations on a tree do not conflict.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static uint64_t get_key(DBT *key) {
+ uint64_t k = 0;
+ assert(key->size == sizeof k);
+ memcpy(&k, key->data, key->size);
+ return htonl(k);
+}
+
+static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+
+ uint64_t k = htonl(i);
+ uint64_t v = i;
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &v, .size = sizeof v };
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+}
+
+struct my_callback_context {
+ DBT key;
+ DBT val;
+};
+
+static int blocking_next_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ DBT const *found_key = a;
+ DBT const *found_val = b;
+ struct my_callback_context *context = (struct my_callback_context *) e;
+ copy_dbt(&context->key, found_key);
+ copy_dbt(&context->val, found_val);
+ return 0;
+}
+
+static void blocking_next(DB_ENV *db_env, DB *db, uint64_t nrows UU(), long sleeptime) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+
+ uint64_t i;
+ for (i = 0; ; i++) {
+ r = cursor->c_getf_next(cursor, 0, blocking_next_callback, &context);
+ if (r != 0)
+ break;
+ if (verbose)
+ printf("%lu next %" PRIu64 "\n", (unsigned long) toku_pthread_self(), get_key(&context.key));
+ usleep(sleeptime);
+ }
+
+ if (verbose)
+ printf("%lu next=%d\n", (unsigned long) toku_pthread_self(), r);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ assert(r == DB_NOTFOUND || r == DB_LOCK_DEADLOCK || r == DB_LOCK_NOTGRANTED);
+#else
+ assert(r == DB_NOTFOUND);
+#endif
+
+ int rr = cursor->c_close(cursor); assert(rr == 0);
+
+ if (r == DB_NOTFOUND) {
+ if (verbose) printf("%lu commit\n", (unsigned long) toku_pthread_self());
+ r = txn->commit(txn, 0);
+ } else {
+ if (verbose) printf("%lu abort\n", (unsigned long) toku_pthread_self());
+ r = txn->abort(txn);
+ }
+ assert(r == 0);
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+static void blocking_prev(DB_ENV *db_env, DB *db, uint64_t nrows UU(), long sleeptime) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+
+ uint64_t i;
+ for (i = 0; ; i++) {
+ r = cursor->c_getf_prev(cursor, 0, blocking_next_callback, &context);
+ if (r != 0)
+ break;
+ if (verbose)
+ printf("%lu prev %" PRIu64 "\n", (unsigned long) toku_pthread_self(), get_key(&context.key));
+ usleep(sleeptime);
+ }
+
+ if (verbose)
+ printf("%lu prev=%d\n", (unsigned long) toku_pthread_self(), r);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ assert(r == DB_NOTFOUND || r == DB_LOCK_DEADLOCK || r == DB_LOCK_NOTGRANTED);
+#else
+ assert(r == DB_NOTFOUND);
+#endif
+
+ int rr = cursor->c_close(cursor); assert(rr == 0);
+
+ if (r == DB_NOTFOUND) {
+ if (verbose) printf("%lu commit\n", (unsigned long) toku_pthread_self());
+ r = txn->commit(txn, 0);
+ } else {
+ if (verbose) printf("%lu abort\n", (unsigned long) toku_pthread_self());
+ r = txn->abort(txn);
+ }
+ assert(r == 0);
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+struct blocking_next_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_next_thread(void *arg) {
+ struct blocking_next_args *a = (struct blocking_next_args *) arg;
+ blocking_next(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime) {
+ int r;
+ toku_pthread_t tids[nthreads];
+ struct blocking_next_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_next_thread, &a); assert(r == 0);
+ }
+ blocking_prev(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 10;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // populate the db
+ populate(db_env, db, nrows);
+
+ run_test(db_env, db, nthreads, nrows, sleeptime);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-prelock-range.cc b/storage/tokudb/PerconaFT/src/tests/blocking-prelock-range.cc
new file mode 100644
index 00000000000..8821b39aa8a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-prelock-range.cc
@@ -0,0 +1,158 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that conflicting range locks works suspend the conflicting threads.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void blocking_range_lock(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
+ int r;
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, DB_RMW); assert(r == 0);
+
+ uint64_t k = 0;
+ DBT key = { .data = &k, .size = sizeof k};
+ r = cursor->c_set_bounds(cursor, &key, &key, true, 0); assert(r == 0);
+
+ usleep(sleeptime);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+}
+
+struct blocking_range_lock_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_range_lock_thread(void *arg) {
+ struct blocking_range_lock_args *a = (struct blocking_range_lock_args *) arg;
+ blocking_range_lock(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 100;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ toku_pthread_t tids[nthreads];
+ struct blocking_range_lock_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_range_lock_thread, &a); assert(r == 0);
+ }
+ blocking_range_lock(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-put-timeout.cc b/storage/tokudb/PerconaFT/src/tests/blocking-put-timeout.cc
new file mode 100644
index 00000000000..a31e173fd83
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-put-timeout.cc
@@ -0,0 +1,189 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that blocking lock waits eventually time out if the lock owner never releases the lock.
+
+// A begin txn
+// A write locks 0
+// A sleeps
+// B begin txn
+// B tries to write lock 0, blocks
+// B's write lock times out, B aborts its txn
+// A wakes up and commits its txn
+
+#include "test.h"
+#include "toku_pthread.h"
+
+struct test_seq {
+ int state;
+ toku_mutex_t lock;
+ toku_cond_t cv;
+};
+
+static void test_seq_init(struct test_seq *seq) {
+ seq->state = 0;
+ toku_mutex_init(&seq->lock, NULL);
+ toku_cond_init(&seq->cv, NULL);
+}
+
+static void test_seq_destroy(struct test_seq *seq) {
+ toku_mutex_destroy(&seq->lock);
+ toku_cond_destroy(&seq->cv);
+}
+
+static void test_seq_sleep(struct test_seq *seq, int new_state) {
+ toku_mutex_lock(&seq->lock);
+ while (seq->state != new_state) {
+ toku_cond_wait(&seq->cv, &seq->lock);
+ }
+ toku_mutex_unlock(&seq->lock);
+}
+
+static void test_seq_next_state(struct test_seq *seq) {
+ toku_mutex_lock(&seq->lock);
+ seq->state++;
+ toku_cond_broadcast(&seq->cv);
+ toku_mutex_unlock(&seq->lock);
+}
+
+static void t_a(DB_ENV *db_env, DB *db, struct test_seq *seq) {
+ int r;
+ test_seq_sleep(seq, 0);
+ int k = 0;
+ DB_TXN *txn_a = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn_a, 0); assert(r == 0);
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &k, .size = sizeof k };
+ r = db->put(db, txn_a, &key, &val, 0); assert(r == 0);
+ test_seq_next_state(seq);
+ sleep(10);
+ r = txn_a->commit(txn_a, 0); assert(r == 0);
+}
+
+static void t_b(DB_ENV *db_env, DB *db, struct test_seq *seq) {
+ int r;
+ test_seq_sleep(seq, 1);
+ int k = 0;
+ DB_TXN *txn_b = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn_b, 0); assert(r == 0);
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &k, .size = sizeof k };
+ r = db->put(db, txn_b, &key, &val, 0);
+ assert(r == DB_LOCK_NOTGRANTED);
+ r = txn_b->abort(txn_b); assert(r == 0);
+}
+
+struct t_a_args {
+ DB_ENV *env;
+ DB *db;
+ struct test_seq *seq;
+};
+
+static void *t_a_thread(void *arg) {
+ struct t_a_args *a = (struct t_a_args *) arg;
+ t_a(a->env, a->db, a->seq);
+ return arg;
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ uint64_t lock_timeout_msec;
+ r = db_env->get_lock_timeout(db_env, &lock_timeout_msec); assert(r == 0);
+ if (verbose) printf("lock timeout: %" PRIu64 "\n", lock_timeout_msec);
+ r = db_env->set_lock_timeout(db_env, 5000, nullptr); assert(r == 0);
+ r = db_env->get_lock_timeout(db_env, &lock_timeout_msec); assert(r == 0);
+ if (verbose) printf("lock timeout: %" PRIu64 "\n", lock_timeout_msec);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // run test
+ struct test_seq seq; ZERO_STRUCT(seq); test_seq_init(&seq);
+ toku_pthread_t t_a_id;
+ struct t_a_args t_a_args = { db_env, db, &seq };
+ r = toku_pthread_create(&t_a_id, NULL, t_a_thread, &t_a_args); assert(r == 0);
+ t_b(db_env, db, &seq);
+ void *ret;
+ r = toku_pthread_join(t_a_id, &ret); assert(r == 0);
+ test_seq_destroy(&seq);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-put-wakeup.cc b/storage/tokudb/PerconaFT/src/tests/blocking-put-wakeup.cc
new file mode 100644
index 00000000000..615cdb5d17c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-put-wakeup.cc
@@ -0,0 +1,184 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that a blocking lock gets granted when the owning transaction commits.
+
+// A begin txn
+// A write locks 0
+// A sleeps
+// B begin txn
+// B tries to write lock 0, blocks
+// A wakes up and commits its txn
+// B's write lock is granted, B's thread resumes,
+// B commits its txn
+
+#include "test.h"
+#include "toku_pthread.h"
+
+struct test_seq {
+ int state;
+ toku_mutex_t lock;
+ toku_cond_t cv;
+};
+
+static void test_seq_init(struct test_seq *seq) {
+ seq->state = 0;
+ toku_mutex_init(&seq->lock, NULL);
+ toku_cond_init(&seq->cv, NULL);
+}
+
+static void test_seq_destroy(struct test_seq *seq) {
+ toku_mutex_destroy(&seq->lock);
+ toku_cond_destroy(&seq->cv);
+}
+
+static void test_seq_sleep(struct test_seq *seq, int new_state) {
+ toku_mutex_lock(&seq->lock);
+ while (seq->state != new_state) {
+ toku_cond_wait(&seq->cv, &seq->lock);
+ }
+ toku_mutex_unlock(&seq->lock);
+}
+
+static void test_seq_next_state(struct test_seq *seq) {
+ toku_mutex_lock(&seq->lock);
+ seq->state++;
+ toku_cond_broadcast(&seq->cv);
+ toku_mutex_unlock(&seq->lock);
+}
+
+static void t_a(DB_ENV *db_env, DB *db, struct test_seq *seq) {
+ int r;
+ test_seq_sleep(seq, 0);
+ int k = 0;
+ DB_TXN *txn_a = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn_a, 0); assert(r == 0);
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &k, .size = sizeof k };
+ r = db->put(db, txn_a, &key, &val, 0); assert(r == 0);
+ test_seq_next_state(seq);
+ sleep(10);
+ r = txn_a->commit(txn_a, 0); assert(r == 0);
+}
+
+static void t_b(DB_ENV *db_env, DB *db, struct test_seq *seq) {
+ int r;
+ test_seq_sleep(seq, 1);
+ int k = 0;
+ DB_TXN *txn_b = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn_b, 0); assert(r == 0);
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &k, .size = sizeof k };
+ r = db->put(db, txn_b, &key, &val, 0); assert(r == 0);
+ r = txn_b->commit(txn_b, 0); assert(r == 0);
+}
+
+struct t_a_args {
+ DB_ENV *env;
+ DB *db;
+ struct test_seq *seq;
+};
+
+static void *t_a_thread(void *arg) {
+ struct t_a_args *a = (struct t_a_args *) arg;
+ t_a(a->env, a->db, a->seq);
+ return arg;
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // run test
+ struct test_seq seq; ZERO_STRUCT(seq); test_seq_init(&seq);
+ toku_pthread_t t_a_id;
+ struct t_a_args t_a_args = { db_env, db, &seq };
+ r = toku_pthread_create(&t_a_id, NULL, t_a_thread, &t_a_args); assert(r == 0);
+ t_b(db_env, db, &seq);
+ void *ret;
+ r = toku_pthread_join(t_a_id, &ret); assert(r == 0);
+ test_seq_destroy(&seq);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-put.cc b/storage/tokudb/PerconaFT/src/tests/blocking-put.cc
new file mode 100644
index 00000000000..0fa56325007
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-put.cc
@@ -0,0 +1,157 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// have multiple threads try to put key 0 into the same db. one thread should gain a write lock on the key.
+// the other threads should block until the thread that owns the lock commits its transaction. then, one
+// of the blocked transactions should gain the lock and its owning thread resumed.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+// pound on key == 0 and hold the write lock for a time less than the lock timeout
+static void blocking_put(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
+ int r;
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ uint64_t k = 0;
+ DBT key = { .data = &k, .size = sizeof k};
+ DBT val = { .data = &k, .size = sizeof k};
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+
+ usleep(sleeptime);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+}
+
+struct blocking_put_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_put_thread(void *arg) {
+ struct blocking_put_args *a = (struct blocking_put_args *) arg;
+ blocking_put(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 100;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ toku_pthread_t tids[nthreads];
+ struct blocking_put_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_put_thread, &a); assert(r == 0);
+ }
+ blocking_put(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-set-range-0.cc b/storage/tokudb/PerconaFT/src/tests/blocking-set-range-0.cc
new file mode 100644
index 00000000000..a0e03ab3293
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-set-range-0.cc
@@ -0,0 +1,213 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that cursor set range operations suspend the conflicting threads when another transaction
+// owns a lock on the key. the test uses keys 0, nrows/2, and nrows-1.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+
+ uint64_t k = htonl(i);
+ uint64_t v = i;
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &v, .size = sizeof v };
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+}
+
+struct my_callback_context {
+ DBT key;
+ DBT val;
+};
+
+static int blocking_set_range_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ DBT const *found_key = a;
+ DBT const *found_val = b;
+ struct my_callback_context *context = (struct my_callback_context *) e;
+ copy_dbt(&context->key, found_key);
+ copy_dbt(&context->val, found_val);
+ return 0;
+}
+
+static void blocking_set_range(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime, uint64_t the_key) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on the key
+
+ uint64_t k = htonl(the_key);
+ DBT key = { .data = &k, .size = sizeof k };
+ r = cursor->c_getf_set_range(cursor, DB_RMW, &key, blocking_set_range_callback, &context); assert(r == 0);
+ uint64_t v;
+ assert(context.val.size == sizeof v);
+ memcpy(&v, context.val.data, context.val.size);
+ assert(v == the_key); // verify the value
+
+ usleep(sleeptime);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+struct blocking_set_range_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+ uint64_t the_key;
+};
+
+static void *blocking_set_range_thread(void *arg) {
+ struct blocking_set_range_args *a = (struct blocking_set_range_args *) arg;
+ blocking_set_range(a->db_env, a->db, a->nrows, a->sleeptime, a->the_key);
+ return arg;
+}
+
+static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime, uint64_t the_key) {
+ int r;
+ toku_pthread_t tids[nthreads];
+ struct blocking_set_range_args a = { db_env, db, nrows, sleeptime, the_key };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_set_range_thread, &a); assert(r == 0);
+ }
+ blocking_set_range(db_env, db, nrows, sleeptime, the_key);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 10;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // populate the db
+ populate(db_env, db, nrows);
+
+ run_test(db_env, db, nthreads, nrows, sleeptime, 0);
+ run_test(db_env, db, nthreads, nrows, sleeptime, nrows/2);
+ run_test(db_env, db, nthreads, nrows, sleeptime, nrows-1);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-set-range-n.cc b/storage/tokudb/PerconaFT/src/tests/blocking-set-range-n.cc
new file mode 100644
index 00000000000..056d74dbdd2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-set-range-n.cc
@@ -0,0 +1,206 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that cursor set range operations suspend the conflicting threads when another transaction
+// owns a lock on the key. the key is at the right edge of the key space.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+
+ uint64_t k = htonl(i);
+ uint64_t v = i;
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &v, .size = sizeof v };
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+}
+
+struct my_callback_context {
+ DBT key;
+ DBT val;
+};
+
+static int blocking_set_range_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ DBT const *found_key = a;
+ DBT const *found_val = b;
+ struct my_callback_context *context = (struct my_callback_context *) e;
+ copy_dbt(&context->key, found_key);
+ copy_dbt(&context->val, found_val);
+ return 0;
+}
+
+static void blocking_set_range(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime, uint64_t the_key) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on the key
+
+ uint64_t k = htonl(the_key);
+ DBT key = { .data = &k, .size = sizeof k };
+ r = cursor->c_getf_set_range(cursor, DB_RMW, &key, blocking_set_range_callback, &context); assert(r == DB_NOTFOUND);
+ usleep(sleeptime);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+struct blocking_set_range_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+ uint64_t the_key;
+};
+
+static void *blocking_set_range_thread(void *arg) {
+ struct blocking_set_range_args *a = (struct blocking_set_range_args *) arg;
+ blocking_set_range(a->db_env, a->db, a->nrows, a->sleeptime, a->the_key);
+ return arg;
+}
+
+static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime, uint64_t the_key) {
+ int r;
+ toku_pthread_t tids[nthreads];
+ struct blocking_set_range_args a = { db_env, db, nrows, sleeptime, the_key };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_set_range_thread, &a); assert(r == 0);
+ }
+ blocking_set_range(db_env, db, nrows, sleeptime, the_key);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 10;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // populate the db
+ populate(db_env, db, nrows);
+
+ run_test(db_env, db, nthreads, nrows, sleeptime, nrows);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-set-range-reverse-0.cc b/storage/tokudb/PerconaFT/src/tests/blocking-set-range-reverse-0.cc
new file mode 100644
index 00000000000..1e7fca85bff
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-set-range-reverse-0.cc
@@ -0,0 +1,211 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that cursor set range reverse operations suspend the conflicting threads when another transaction
+// owns a lock on the key.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+
+ uint64_t k = htonl(i);
+ uint64_t v = i;
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &v, .size = sizeof v };
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+}
+
+struct my_callback_context {
+ DBT key;
+ DBT val;
+};
+
+static int blocking_set_range_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ DBT const *found_key = a;
+ DBT const *found_val = b;
+ struct my_callback_context *context = (struct my_callback_context *) e;
+ copy_dbt(&context->key, found_key);
+ copy_dbt(&context->val, found_val);
+ return 0;
+}
+
+static void blocking_set_range(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime, uint64_t the_key) {
+ int r;
+
+ struct my_callback_context context;
+ dbt_init_realloc(&context.key);
+ dbt_init_realloc(&context.val);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, DB_RMW); assert(r == 0); // get a write lock on the key
+
+ uint64_t k = htonl(the_key);
+ DBT key = { .data = &k, .size = sizeof k };
+ r = cursor->c_getf_set_range_reverse(cursor, 0, &key, blocking_set_range_callback, &context); assert(r == 0);
+ uint64_t v;
+ assert(context.val.size == sizeof v);
+ memcpy(&v, context.val.data, context.val.size);
+ assert(v == 0); // verify the value
+
+ usleep(sleeptime);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+
+ toku_free(context.key.data);
+ toku_free(context.val.data);
+}
+
+struct blocking_set_range_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+ uint64_t the_key;
+};
+
+static void *blocking_set_range_thread(void *arg) {
+ struct blocking_set_range_args *a = (struct blocking_set_range_args *) arg;
+ blocking_set_range(a->db_env, a->db, a->nrows, a->sleeptime, a->the_key);
+ return arg;
+}
+
+static void run_test(DB_ENV *db_env, DB *db, int nthreads, uint64_t nrows, long sleeptime, uint64_t the_key) {
+ int r;
+ toku_pthread_t tids[nthreads];
+ struct blocking_set_range_args a = { db_env, db, nrows, sleeptime, the_key };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_set_range_thread, &a); assert(r == 0);
+ }
+ blocking_set_range(db_env, db, nrows, sleeptime, the_key);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 10;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // populate the db
+ populate(db_env, db, nrows);
+
+ run_test(db_env, db, nthreads, nrows, sleeptime, 0);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-set.cc b/storage/tokudb/PerconaFT/src/tests/blocking-set.cc
new file mode 100644
index 00000000000..ba64989842a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-set.cc
@@ -0,0 +1,200 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that cursor set operations suspend the conflicting threads when another transaction
+// owns a lock on the key.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void populate(DB_ENV *db_env, DB *db, uint64_t nrows) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+
+ uint64_t k = htonl(i);
+ uint64_t v = i;
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &v, .size = sizeof v };
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+}
+
+static int blocking_set_callback(DBT const *a UU(), DBT const *b UU(), void *e UU()) {
+ // DBT const *found_key = a;
+ DBT const *found_val = b;
+ DBT *my_val = (DBT *) e;
+ assert(my_val->flags == DB_DBT_REALLOC);
+ my_val->data = toku_xrealloc(my_val->data, found_val->size);
+ my_val->size = found_val->size;
+ memcpy(my_val->data, found_val->data, found_val->size);
+ return 0;
+}
+
+static void blocking_set(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
+ int r;
+
+ DBT val;
+ dbt_init_realloc(&val);
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0); // get a write lock on the key
+
+ uint64_t k = htonl(0); // set to key 0
+ DBT key = { .data = &k, .size = sizeof k };
+ r = cursor->c_getf_set(cursor, DB_RMW, &key, blocking_set_callback, &val); assert(r == 0);
+ uint64_t v;
+ assert(val.size == sizeof v);
+ memcpy(&v, val.data, val.size);
+ assert(v == 0); // verify the value
+
+ usleep(sleeptime);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+
+ toku_free(val.data);
+}
+
+struct blocking_set_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_set_thread(void *arg) {
+ struct blocking_set_args *a = (struct blocking_set_args *) arg;
+ blocking_set(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 100;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ // populate the db
+ populate(db_env, db, nrows);
+
+ toku_pthread_t tids[nthreads];
+ struct blocking_set_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_set_thread, &a); assert(r == 0);
+ }
+ blocking_set(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/blocking-table-lock.cc b/storage/tokudb/PerconaFT/src/tests/blocking-table-lock.cc
new file mode 100644
index 00000000000..9ae33c56621
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/blocking-table-lock.cc
@@ -0,0 +1,151 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that table locks used by multiple transactions suspend the conflicting thread rather than just return DB_LOCK_NOTGRANTED.
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void blocking_table_lock(DB_ENV *db_env, DB *db, uint64_t nrows, long sleeptime) {
+ int r;
+
+ for (uint64_t i = 0; i < nrows; i++) {
+ DB_TXN *txn = NULL;
+ r = db_env->txn_begin(db_env, NULL, &txn, 0); assert(r == 0);
+
+ r = db->pre_acquire_table_lock(db, txn); assert(r == 0);
+
+ usleep(sleeptime);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ if (verbose)
+ printf("%lu %" PRIu64 "\n", (unsigned long) toku_pthread_self(), i);
+ }
+}
+
+struct blocking_table_lock_args {
+ DB_ENV *db_env;
+ DB *db;
+ uint64_t nrows;
+ long sleeptime;
+};
+
+static void *blocking_table_lock_thread(void *arg) {
+ struct blocking_table_lock_args *a = (struct blocking_table_lock_args *) arg;
+ blocking_table_lock(a->db_env, a->db, a->nrows, a->sleeptime);
+ return arg;
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ uint64_t nrows = 100;
+ int nthreads = 2;
+ long sleeptime = 100000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "test.db";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--nthreads") == 0 && i+1 < argc) {
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--sleeptime") == 0 && i+1 < argc) {
+ sleeptime = atol(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, NULL, db_filename, NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT|DB_THREAD, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+
+ toku_pthread_t tids[nthreads];
+ struct blocking_table_lock_args a = { db_env, db, nrows, sleeptime };
+ for (int i = 0; i < nthreads-1; i++) {
+ r = toku_pthread_create(&tids[i], NULL, blocking_table_lock_thread, &a); assert(r == 0);
+ }
+ blocking_table_lock(db_env, db, nrows, sleeptime);
+ for (int i = 0; i < nthreads-1; i++) {
+ void *ret;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0);
+ }
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/bug1381.cc b/storage/tokudb/PerconaFT/src/tests/bug1381.cc
new file mode 100644
index 00000000000..0e59cabf345
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/bug1381.cc
@@ -0,0 +1,189 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test for #1381: If we insert into a locked empty table, not much goes into the rollback data structure. */
+
+#include <db.h>
+#include <sys/stat.h>
+#include <memory.h>
+
+static int generate_row_for_put(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *dest_key_arrays,
+ DBT_ARRAY *dest_val_arrays,
+ const DBT *src_key,
+ const DBT *src_val
+ )
+{
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+
+ dest_key->data = src_key->data;
+ dest_key->size = src_key->size;
+ dest_val->data = src_val->data;
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+
+static void do_1381_maybe_lock (int do_loader, uint64_t *raw_count) {
+ int r;
+ DB_TXN * const null_txn = 0;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+
+ // Create an empty file
+ {
+ DB_ENV *env;
+ DB *db;
+
+ const int envflags = DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK|DB_THREAD|DB_PRIVATE;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, generate_row_for_put); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, "main", 0, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ }
+ // Now open the empty file and insert
+ {
+ DB_ENV *env;
+ DB *db;
+ const int envflags = DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK|DB_THREAD |DB_PRIVATE;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, generate_row_for_put); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, "main", 0, DB_BTREE, 0, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ uint32_t mult_put_flags = 0;
+ uint32_t mult_dbt_flags = 0;
+ DB_LOADER* loader = NULL;
+ if (do_loader) {
+ r = env->create_loader(
+ env,
+ txn,
+ &loader,
+ NULL, // no src_db needed
+ 1,
+ &db,
+ &mult_put_flags,
+ &mult_dbt_flags,
+ LOADER_COMPRESS_INTERMEDIATES
+ );
+ CKERR(r);
+ }
+
+ struct txn_stat *s1, *s2;
+ r = txn->txn_stat(txn, &s1); CKERR(r);
+
+ {
+ DBT key;
+ dbt_init(&key, "hi", 3);
+ DBT val;
+ dbt_init(&val, "v", 2);
+ if (do_loader) {
+ r = loader->put(loader, &key, &val);
+ CKERR(r);
+ }
+ else {
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ }
+ }
+ if (do_loader) {
+ r = loader->close(loader);
+ CKERR(r);
+ }
+
+ r = txn->txn_stat(txn, &s2); CKERR(r);
+ //printf("Raw counts = %" PRId64 ", %" PRId64 "\n", s1->rollback_raw_count, s2->rollback_raw_count);
+
+ *raw_count = s2->rollback_raw_count - s1->rollback_raw_count;
+ if (do_loader) {
+ assert(s1->rollback_raw_count < s2->rollback_raw_count);
+ assert(s1->rollback_num_entries + 1 == s2->rollback_num_entries);
+ } else {
+ assert(s1->rollback_raw_count < s2->rollback_raw_count);
+ assert(s1->rollback_num_entries < s2->rollback_num_entries);
+ }
+
+ toku_free(s1); toku_free(s2);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ }
+}
+
+static void
+do_1381 (void) {
+ int do_table_lock;
+ uint64_t raw_counts[2];
+ for (do_table_lock = 0; do_table_lock < 2 ; do_table_lock++) {
+ do_1381_maybe_lock(do_table_lock, &raw_counts[do_table_lock]);
+ }
+ assert(raw_counts[0] > raw_counts[1]); // the raw counts should be less for the tablelock case.
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ parse_args(argc, argv);
+ do_1381();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/cachetable-race.cc b/storage/tokudb/PerconaFT/src/tests/cachetable-race.cc
new file mode 100644
index 00000000000..54d78c05f98
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/cachetable-race.cc
@@ -0,0 +1,152 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* This test is a little bit ugly, but I think it's mostly harmless.
+ * It's ugly because it requires a hook into cachetable.c, and that
+ * hook is a little ugly.
+ *
+ * The way it works:
+ *
+ * In the cachetable we set a variable that indicates that we are
+ * saving user data. (This is toku_checkpointing_user_data_status).
+ *
+ * In this test (cachetable-race), we close 2 of 5 dbs, and then busy
+ * wait for that status variable to go to 1 and close the others.
+ *
+ * In this way, the close happens (with good chances) right in the
+ * middle of the checkpoint loop for saving all the user data, which
+ * seems to trigger the race error.
+ */
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb.h"
+#include "ydb-internal.h"
+
+DB_ENV *env;
+enum {NUM_DBS=5};
+
+const char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+
+static void run_cachetable_race_test(void)
+{
+ int r;
+ toku_os_recursive_delete(env_dir);
+ r = toku_os_mkdir(env_dir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 5); CKERR(r);
+
+ enum {MAX_NAME=128};
+ char name[MAX_NAME];
+
+ DB **XMALLOC_N(NUM_DBS, dbs);
+ int idx[NUM_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ }
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+
+ if (i==2) {
+ if (verbose) printf("%s:%d c=%d\n", __FILE__, __LINE__, toku_test_get_checkpointing_user_data_status());
+ while (toku_test_get_checkpointing_user_data_status()==0)
+ sched_yield();
+ if (verbose) printf("%s:%d c=%d\n", __FILE__, __LINE__, toku_test_get_checkpointing_user_data_status());
+ }
+ }
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_cachetable_race_test();
+
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: [-h] [-v] [-q] -s\n%s\n", cmd);
+ fprintf(stderr, " where -h print this message\n");
+ fprintf(stderr, " -v verbose (multiple times for more verbosity)\n");
+ fprintf(stderr, " -q quiet (default is verbosity==1)\n");
+ fprintf(stderr, " -e <env> uses <env> to construct the directory (so that different tests can run concurrently)\n");
+ fprintf(stderr, " -s use size factor of 1 and count temporary files\n");
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-s")==0) {
+ printf("\nTesting loader with size_factor=1\n");
+ db_env_set_loader_size_factor(1);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/checkpoint1.cc b/storage/tokudb/PerconaFT/src/tests/checkpoint1.cc
new file mode 100644
index 00000000000..9f9337b36cb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/checkpoint1.cc
@@ -0,0 +1,94 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/stat.h>
+
+/* basic checkpoint testing. Do things end up in the log? */
+
+DB_ENV *env;
+DB *db;
+DB_TXN *txn;
+
+static void
+insert(int i)
+{
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ DBT key, val;
+ int r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&val, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void
+checkpoint1 (void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(0);
+ r=env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ parse_args(argc, argv);
+ checkpoint1();
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/checkpoint_fairness.cc b/storage/tokudb/PerconaFT/src/tests/checkpoint_fairness.cc
new file mode 100644
index 00000000000..be82f8c352f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/checkpoint_fairness.cc
@@ -0,0 +1,133 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// This test fails if the multi_operation_lock prefers readers. (See #4347).
+// But works well if the multi_operation_lock prefers writers (which, since there is typically only one writer, makes it fair).
+// What this test does:
+// Starts a bunch of threads (100 seems to work): Each executes many transactions (and thus obtains the multi_operation_lock during the txn->commit, and until #4346 is changed, holds it through the fsync. If we fix #4346 then
+// this test may not be sensitive to the bug.)
+// Meanwhile another thread tries to do W checkpoints. (W=10 seems to work).
+// The checkpoint thread waits until all the transaction threads have gotten going (waits until each transaction thread has done 10 transactions).
+// The transaction threads get upset if they manage to run for 1000 transactions without the W checkpoints being finished.
+// The theory is that the transaction threads can starve the checkpoint thread by obtaining the multi_operation_lock.
+// But making the multi_operation_lock prefer writers means that the checkpoint gets a chance to run.
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <portability/toku_atomic.h>
+
+DB_ENV *env;
+DB *db;
+const char *env_dir = TOKU_TEST_FILENAME;
+
+const int n_threads = 100;
+volatile int reader_start_count = 0;
+
+const int W = 10;
+volatile int writer_done_count = 0;
+
+static void *start_txns (void *e) {
+ int *CAST_FROM_VOIDP(idp, e);
+ int id = *idp;
+ int j;
+ DBT k;
+ dbt_init(&k, &id, sizeof id);
+ for (j=0; writer_done_count<W; j++) { // terminate the loop when the checkpoint thread has done it's W items.
+ DB_TXN *txn;
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->put(db, txn, &k, &k, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn, 0); CKERR(chk_r); }
+ if (j==10) (void)toku_sync_fetch_and_add(&reader_start_count, 1);
+ if (j%1000==999) { printf("."); fflush(stdout); }
+ assert(j<1000); // Get upset if we manage to run this many transactions without the checkpoint thread
+ }
+ if (verbose) printf("rdone j=%d\n", j);
+ return NULL;
+}
+static void start_checkpoints (void) {
+ while (reader_start_count < n_threads) { sched_yield(); }
+ for (int i=0; i<W; i++) {
+ if (verbose) printf("cks\n");
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+ if (verbose) printf("ck\n");
+ sched_yield();
+ (void)toku_sync_fetch_and_add(&writer_done_count, 1);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+
+ // try to starve the checkpoint
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ { int chk_r = env->set_redzone(env, 0); CKERR(chk_r); }
+ {
+ const int size = 10+strlen(env_dir);
+ char cmd[size];
+ snprintf(cmd, size, "rm -rf %s", env_dir);
+ int r = system(cmd);
+ CKERR(r);
+ }
+ { int chk_r = toku_os_mkdir(env_dir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER;
+ { int chk_r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+
+ { int chk_r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+
+ pthread_t thds[n_threads];
+ int ids[n_threads];
+ for (int i=0; i<n_threads; i++) {
+ ids[i]=i;
+ { int chk_r = toku_pthread_create(&thds[i], NULL, start_txns, &ids[i]); CKERR(chk_r); }
+ }
+ start_checkpoints();
+
+ for (int i=0; i<n_threads; i++) {
+ void *retval;
+ { int chk_r = toku_pthread_join(thds[i], &retval); CKERR(chk_r); }
+ assert(retval==NULL);
+ }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/checkpoint_stress.cc b/storage/tokudb/PerconaFT/src/tests/checkpoint_stress.cc
new file mode 100644
index 00000000000..221471a8045
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/checkpoint_stress.cc
@@ -0,0 +1,375 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "checkpoint_test.h"
+
+
+/***
+
+
+Purpose of this test is to stress the checkpoint logic.
+
+Multiple dictionaries are used. Data is inserted, checkpoints are taken,
+and this test verifies that all checkpoints are valid.
+
+
+Parameters:
+ -cC crash or not (not crashing useful for running valgrind)
+ -i # iteration number (default is to run 5 iterations)
+ -n # number of operations per iteration (default 5001)
+ -v verbose
+ -q quiet
+
+Each iteration does:
+ - Verify that previous two iterations were correctly executed
+ - Previous inserts were done correctly
+ - There are no rows after last expected
+ - Take checkpoint
+ - Scribble over database (verifying that changes after checkpoint are not effective)
+ - Spawn another thread to perform random acts (inserts/deletes/queries) to
+ simulate normal database operations
+ - Drop dead
+
+***/
+
+#define NUM_DICTIONARIES 4 // any more than 3 is overkill to exercise linked list logic
+
+static int oper_per_iter = 5001; // not-very-nice odd number (not a multiple of a power of two)
+static int do_log_recover = 0;
+
+static toku_pthread_t thread;
+
+// scribble over database to make sure that changes made after checkpoint are not saved
+static void UU()
+scribble(DB* db, int iter) {
+ int64_t firstkey; // first key to verify/insert
+ int64_t numkeys; // number of keys to verify/insert
+
+ if (iter > 0){
+ if (iter == 1) {
+ firstkey = 0;
+ numkeys = oper_per_iter;
+ }
+ else {
+ firstkey = (iter - 2) * oper_per_iter;
+ numkeys = oper_per_iter * 2;
+ }
+ }
+
+ // now insert new rows for this iteration
+ firstkey = iter * oper_per_iter;
+ numkeys = oper_per_iter;
+
+ insert_n_broken(db, NULL, NULL, firstkey, numkeys);
+}
+
+// scribble over database to make sure that changes made after checkpoint are not saved
+// by deleting three of every four rows
+static void UU()
+thin_out(DB* db, int iter) {
+ int64_t firstkey; // first key to verify/insert
+ int64_t numkeys; // number of keys to verify/insert
+
+ if (iter > 0){
+ if (iter == 1) {
+ firstkey = 0;
+ numkeys = oper_per_iter;
+ }
+ else {
+ firstkey = (iter - 2) * oper_per_iter;
+ numkeys = oper_per_iter * 2;
+ }
+ }
+
+ int r;
+ DBT keydbt;
+ int64_t key;
+ DB_TXN * txn;
+
+ dbt_init(&keydbt, &key, sizeof(key));
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = db->pre_acquire_table_lock(db, txn);
+ CKERR(r);
+
+ // now delete three of four rows
+ firstkey = iter * oper_per_iter;
+ numkeys = oper_per_iter;
+
+ for (key = firstkey; key < (firstkey + numkeys); key++) {
+ if (key & 0x03) { // leave every fourth key alone
+ r = db->del(db, txn, &keydbt, DB_DELETE_ANY);
+ CKERR(r);
+ }
+ }
+
+ if ( !do_log_recover )
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+}
+
+
+
+static void
+drop_dead(void) {
+ // deliberate zerodivide or sigsegv
+ fprintf(stderr, "HAPPY CRASH\n");
+ fflush(stdout);
+ fflush(stderr);
+ toku_hard_crash_on_purpose();
+ printf("This line should never be printed\n");
+ fflush(stdout);
+}
+
+
+static void
+verify_and_insert (DB* db, int iter) {
+
+ int64_t firstkey; // first key to verify/insert
+ int64_t numkeys; // number of keys to verify/insert
+
+ if (iter > 0){
+ if (iter == 1) {
+ firstkey = 0;
+ numkeys = oper_per_iter;
+ }
+ else {
+ firstkey = (iter - 2) * oper_per_iter;
+ numkeys = oper_per_iter * 2;
+ }
+ verify_sequential_rows(db, firstkey, numkeys);
+ }
+
+ // now insert new rows for this iteration
+ firstkey = iter * oper_per_iter;
+ numkeys = oper_per_iter;
+
+ int r;
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ insert_n_fixed(db, NULL, NULL, firstkey, numkeys);
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+
+// Purpose of this function is to perform a variety of random acts.
+// This will simulate normal database operations. The idea is for the
+// the crash to occur sometimes during an insert, sometimes during a query, etc.
+static void *
+random_acts(void * d) {
+ void * intothevoid = NULL;
+ DICTIONARY dictionaries = (DICTIONARY) d;
+ if (verbose)
+ printf("perform random acts, %s\n", dictionaries[0].filename);
+ fflush(stdout);
+ int i = 0;
+ int64_t k = 0;
+
+ while (1) { // run until crash
+ // main thread is scribbling over dictionary 0
+ // this thread will futz with other dictionaries
+ for (i = 1; i < NUM_DICTIONARIES; i++) {
+ int j;
+ DB * db = dictionaries[i].db;
+ insert_random(db, NULL, NULL);
+ delete_both_random(db, NULL, NULL, 0); // delete only if found (performs query)
+ delete_both_random(db, NULL, NULL, DB_DELETE_ANY); // delete whether or not found (no query)
+ for (j = 0; j < 10; j++) {
+ delete_fixed(db, NULL, NULL, k, 0); // delete only if found to provoke more queries
+ k++;
+ }
+ }
+ }
+
+ return intothevoid;
+}
+
+uint64_t max_cachesize = 256 << 20;
+
+static void
+run_test (int iter, int die) {
+
+ uint32_t flags = 0;
+
+ int i;
+
+ if (iter == 0)
+ dir_create(TOKU_TEST_FILENAME); // create directory if first time through
+
+ // Run with cachesize of 256 bytes per iteration
+ // to force lots of disk I/O
+ // (each iteration inserts about 4K rows/dictionary, 16 bytes/row, 4 dictionaries = 256K bytes inserted per iteration)
+ const int32_t K256 = 256 * 1024;
+ uint64_t cachebytes = 0;
+ cachebytes = K256 * (iter + 1) - (128 * 1024);
+ if (cachebytes > max_cachesize)
+ cachebytes = 0;
+ if (iter & 2) cachebytes = 0; // use default cachesize half the time
+
+ if (verbose)
+ printf("checkpoint_stress: iter = %d, cachesize (bytes) = 0x%08" PRIx64 "\n", iter, cachebytes);
+
+ int recovery_flags = 0;
+ if ( do_log_recover ) {
+ recovery_flags += DB_INIT_LOG|DB_INIT_TXN;
+ if ( iter != 0 )
+ recovery_flags += DB_RECOVER;
+ }
+ env_startup(TOKU_TEST_FILENAME, cachebytes, recovery_flags);
+
+ // create array of dictionaries
+ // for each dictionary verify previous iterations and perform new inserts
+
+ DICTIONARY_S dictionaries[NUM_DICTIONARIES];
+ for (i = 0; i < NUM_DICTIONARIES; i++) {
+ char name[32];
+ sprintf(name, "stress_%d", i);
+ init_dictionary(&dictionaries[i], flags, name);
+ db_startup(&dictionaries[i], NULL);
+ DB* db = dictionaries[i].db;
+ verify_and_insert(db, iter);
+ }
+
+ // take checkpoint (all dictionaries)
+ snapshot(NULL, 1);
+
+ if (die) {
+ // separate thread will perform random acts on other dictionaries (not 0)
+ int r = toku_pthread_create(&thread, 0, random_acts, (void *) dictionaries);
+ CKERR(r);
+ // this thead will scribble over dictionary 0 before crash to verify that
+ // post-checkpoint inserts are not in the database
+ DB* db = dictionaries[0].db;
+ if (iter & 1)
+ scribble(db, iter);
+ else
+ thin_out(db, iter);
+ uint32_t delay = myrandom();
+ delay &= 0xFFF; // select lower 12 bits, shifted up 8 for random number ...
+ delay = delay << 8; // ... uniformly distributed between 0 and 1M ...
+ usleep(delay); // ... to sleep up to one second (1M usec)
+ drop_dead();
+ }
+ else {
+ for (i = 0; i < NUM_DICTIONARIES; i++) {
+ db_shutdown(&dictionaries[i]);
+ }
+ env_shutdown();
+ }
+}
+
+
+static void
+usage(char *progname) {
+ fprintf(stderr, "Usage:\n%s [-c] [-C] [-i N] [-n N] [-l] [-q|-v]\n"
+ " \n%s [-h]\n", progname,
+ progname);
+}
+
+
+int
+test_main (int argc, char * const argv[]) {
+
+ // get arguments, set parameters
+
+ int iter = -1;
+
+ int c;
+ int crash = 0;
+ while ((c = getopt(argc, (char * const *)argv, "cChi:qvn:lX:")) != -1) {
+ switch(c) {
+ case 'c':
+ crash = 1;
+ break;
+ case 'C':
+ crash = 0;
+ break;
+ case 'i':
+ iter = atoi(optarg);
+ break;
+ case 'n':
+ oper_per_iter = atoi(optarg);
+ break;
+ case 'l':
+ do_log_recover = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'q':
+ verbose--;
+ if (verbose<0) verbose=0;
+ break;
+ case 'X':
+ if (strcmp(optarg, "novalgrind") == 0) {
+ // provide a way for the shell script runner to pass an
+ // arg that suppresses valgrind on this child process
+ break;
+ }
+ // otherwise, fall through to an error
+ case 'h':
+ case '?':
+ usage(argv[0]);
+ return 1;
+ default:
+ assert(false);
+ return 1;
+ }
+ }
+ if (argc!=optind) { usage(argv[0]); return 1; }
+
+ // for developing this test and for exercising with valgrind (no crash)
+ if (iter <0) {
+ if (verbose)
+ printf("No argument, just run five times without crash\n");
+ for (iter = 0; iter<5; iter++) {
+ run_test(iter, 0);
+ }
+ }
+ else {
+ run_test(iter, crash);
+ }
+
+ return 0;
+
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/checkpoint_test.h b/storage/tokudb/PerconaFT/src/tests/checkpoint_test.h
new file mode 100644
index 00000000000..9a8d24c2188
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/checkpoint_test.h
@@ -0,0 +1,484 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+DB_ENV *env;
+
+enum {MAX_NAME=128};
+
+enum {NUM_FIXED_ROWS=1025}; // 4K + 1
+
+typedef struct {
+ DB* db;
+ uint32_t flags;
+ char filename[MAX_NAME]; //Relative to envdir/
+ int num;
+} DICTIONARY_S, *DICTIONARY;
+
+
+static inline int64_t
+generate_val(int64_t key) {
+ int64_t val = key + 314;
+ return val;
+}
+
+// return 0 if same
+static int
+verify_identical_dbts(const DBT *dbt1, const DBT *dbt2) {
+ int r = 0;
+ if (dbt1->size != dbt2->size) r = 1;
+ else if (memcmp(dbt1->data, dbt2->data, dbt1->size)!=0) r = 1;
+ return r;
+}
+
+// return 0 if same
+static int UU()
+compare_dbs(DB *compare_db1, DB *compare_db2) {
+ //This does not lock the dbs/grab table locks.
+ //This means that you CANNOT CALL THIS while another thread is modifying the db.
+ //You CAN call it while a txn is open however.
+ int rval = 0;
+ DB_TXN *compare_txn;
+ int r, r1, r2;
+ r = env->txn_begin(env, NULL, &compare_txn, DB_READ_UNCOMMITTED);
+ CKERR(r);
+ DBC *c1;
+ DBC *c2;
+ r = compare_db1->cursor(compare_db1, compare_txn, &c1, 0);
+ CKERR(r);
+ r = compare_db2->cursor(compare_db2, compare_txn, &c2, 0);
+ CKERR(r);
+
+ DBT key1, val1;
+ DBT key2, val2;
+
+ dbt_init_realloc(&key1);
+ dbt_init_realloc(&val1);
+ dbt_init_realloc(&key2);
+ dbt_init_realloc(&val2);
+
+ do {
+ r1 = c1->c_get(c1, &key1, &val1, DB_NEXT);
+ r2 = c2->c_get(c2, &key2, &val2, DB_NEXT);
+ assert(r1==0 || r1==DB_NOTFOUND);
+ assert(r2==0 || r2==DB_NOTFOUND);
+ if (r1!=r2) rval = 1;
+ else if (r1==0 && r2==0) {
+ //Both found
+ rval = verify_identical_dbts(&key1, &key2) |
+ verify_identical_dbts(&val1, &val2);
+ }
+ } while (r1==0 && r2==0 && rval==0);
+ c1->c_close(c1);
+ c2->c_close(c2);
+ if (key1.data) toku_free(key1.data);
+ if (val1.data) toku_free(val1.data);
+ if (key2.data) toku_free(key2.data);
+ if (val2.data) toku_free(val2.data);
+ compare_txn->commit(compare_txn, 0);
+ return rval;
+}
+
+
+static void UU()
+dir_create(const char *envdir) {
+ int r;
+ toku_os_recursive_delete(envdir);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+}
+
+// pass in zeroes for default cachesize
+static void UU()
+env_startup(const char *envdir, int64_t bytes, int recovery_flags) {
+ int r;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp);
+ CKERR(r);
+ if (bytes) {
+ r = env->set_cachesize(env, bytes >> 30, bytes % (1<<30), 1);
+ CKERR(r);
+ }
+ int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | recovery_flags;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 0); //Disable auto-checkpointing.
+ CKERR(r);
+}
+
+static void UU()
+env_shutdown(void) {
+ int r;
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+static void UU()
+fill_name(DICTIONARY d, char *buf, int bufsize) {
+ int bytes;
+ bytes = snprintf(buf, bufsize, "%s_%08x", d->filename, d->num);
+ assert(bytes>0);
+ assert(bytes>(int)strlen(d->filename));
+ assert(bytes<bufsize);
+ assert(buf[bytes] == 0);
+}
+
+static void UU()
+fill_full_name(const char *envdir, DICTIONARY d, char *buf, int bufsize) {
+ int bytes;
+ bytes = snprintf(buf, bufsize, "%s/%s_%08x", envdir, d->filename, d->num);
+ assert(bytes>0);
+ assert(bytes>(int)strlen(d->filename));
+ assert(bytes<bufsize);
+ assert(buf[bytes] == 0);
+}
+
+static void UU()
+db_startup(DICTIONARY d, DB_TXN *open_txn) {
+ int r;
+ r = db_create(&d->db, env, 0);
+ CKERR(r);
+ DB *db = d->db;
+ if (d->flags) {
+ r = db->set_flags(db, d->flags);
+ CKERR(r);
+ }
+ //Want to simulate much larger test.
+ //Small nodesize means many nodes.
+ db->set_pagesize(db, 1<<10);
+ {
+ char name[MAX_NAME*2];
+ fill_name(d, name, sizeof(name));
+ r = db->open(db, open_txn, name, NULL, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ }
+ {
+ DBT desc;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ dbt_init(&desc, "foo", sizeof("foo"));
+ { int chk_r = db->change_descriptor(db, txn_desc, &desc,0); CKERR(chk_r); }
+ });
+ }
+}
+
+static void UU()
+db_shutdown(DICTIONARY d) {
+ int r;
+ r = d->db->close(d->db, 0);
+ CKERR(r);
+ d->db = NULL;
+}
+
+static void UU()
+null_dictionary(DICTIONARY d) {
+ memset(d, 0, sizeof(*d));
+}
+
+static void UU()
+init_dictionary(DICTIONARY d, uint32_t flags, const char *name) {
+ null_dictionary(d);
+ d->flags = flags;
+ strcpy(d->filename, name);
+}
+
+
+static void UU()
+db_delete(DICTIONARY d) {
+ db_shutdown(d);
+ int r;
+ {
+ char name[MAX_NAME*2];
+ fill_name(d, name, sizeof(name));
+ r = env->dbremove(env, NULL, name, NULL, 0);
+ CKERR(r);
+ }
+ null_dictionary(d);
+}
+
+// Create a new dictionary (dest) with a new dname that has same contents as given dictionary (src).
+// Method:
+// create new dictionary
+// close new dictionary
+// get inames of both dictionaries
+// copy file (by iname) of src to dest
+// open dest dictionary
+static void UU()
+dbcpy(const char *envdir, DICTIONARY dest, DICTIONARY src, DB_TXN *open_txn) {
+ int r;
+
+ assert(dest->db == NULL);
+ *dest = *src;
+ dest->db = NULL;
+ dest->num++;
+
+ db_startup(dest, open_txn);
+ db_shutdown(dest);
+
+ char dest_dname[MAX_NAME*2];
+ fill_name(dest, dest_dname, sizeof(dest_dname));
+
+ char src_dname[MAX_NAME*2];
+ fill_name(src, src_dname, sizeof(src_dname));
+
+ DBT dest_dname_dbt;
+ DBT dest_iname_dbt;
+ DBT src_dname_dbt;
+ DBT src_iname_dbt;
+
+ dbt_init(&dest_dname_dbt, dest_dname, strlen(dest_dname)+1);
+ dbt_init(&dest_iname_dbt, NULL, 0);
+ dest_iname_dbt.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dest_dname_dbt, &dest_iname_dbt);
+ CKERR(r);
+
+ dbt_init(&src_dname_dbt, src_dname, strlen(src_dname)+1);
+ dbt_init(&src_iname_dbt, NULL, 0);
+ src_iname_dbt.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &src_dname_dbt, &src_iname_dbt);
+ CKERR(r);
+
+ char * CAST_FROM_VOIDP(src_iname, src_iname_dbt.data);
+ char * CAST_FROM_VOIDP(dest_iname, dest_iname_dbt.data);
+
+ int bytes;
+
+ char command[sizeof("cp -f ") + strlen(src_iname)+ 2 * (strlen(envdir) + strlen("/ ")) + strlen(dest_iname)];
+ bytes = snprintf(command, sizeof(command), "cp -f %s/%s %s/%s", envdir, src_iname, envdir, dest_iname);
+ assert(bytes<(int)sizeof(command));
+
+ toku_free(src_iname);
+ toku_free(dest_iname);
+
+ r = system(command);
+ CKERR(r);
+ db_startup(dest, open_txn);
+}
+
+static void UU()
+db_replace(const char *envdir, DICTIONARY d, DB_TXN *open_txn) {
+ //Replaces a dictionary with a physical copy that is reopened.
+ //Filename is changed by incrementing the number.
+ //This should be equivalent to 'rollback to checkpoint'.
+ //The DB* disappears.
+ DICTIONARY_S temp;
+ null_dictionary(&temp);
+ dbcpy(envdir, &temp, d, open_txn);
+ db_delete(d);
+ *d = temp;
+}
+
+static void UU()
+insert_random(DB *db1, DB *db2, DB_TXN *txn) {
+ int64_t v = random();
+ int64_t k = ((int64_t)(random()) << 32) + v;
+ int r;
+ DBT key;
+ DBT val;
+ dbt_init(&key, &k, sizeof(k));
+ dbt_init(&val, &v, sizeof(v));
+
+ if (db1) {
+ r = db1->put(db1, txn, &key, &val, 0);
+ CKERR(r);
+ }
+ if (db2) {
+ r = db2->put(db2, txn, &key, &val, 0);
+ CKERR(r);
+ }
+}
+
+static void UU()
+delete_both_random(DB *db1, DB *db2, DB_TXN *txn, uint32_t flags) {
+ int64_t k = random64();
+ int r;
+ DBT key;
+ dbt_init(&key, &k, sizeof(k));
+
+ if (db1) {
+ r = db1->del(db1, txn, &key, flags);
+ CKERR2s(r, 0, DB_NOTFOUND);
+ }
+ if (db2) {
+ r = db2->del(db2, txn, &key, flags);
+ CKERR2s(r, 0, DB_NOTFOUND);
+ }
+}
+
+
+
+static void UU()
+delete_fixed(DB *db1, DB *db2, DB_TXN *txn, int64_t k, uint32_t flags) {
+ int r;
+ DBT key;
+
+ dbt_init(&key, &k, sizeof(k));
+
+ if (db1) {
+ r = db1->del(db1, txn, &key, flags);
+ CKERR2s(r, 0, DB_NOTFOUND);
+ }
+ if (db2) {
+ r = db2->del(db2, txn, &key, flags);
+ CKERR2s(r, 0, DB_NOTFOUND);
+ }
+}
+
+static void UU()
+delete_n(DB *db1, DB *db2, DB_TXN *txn, int firstkey, int n, uint32_t flags) {
+ int i;
+ for (i=0;i<n;i++) {
+ delete_fixed(db1, db2, txn, firstkey+i, flags);
+ }
+}
+
+static void
+insert_n(DB *db1, DB *db2, DB_TXN *txn, int firstkey, int n, int offset) {
+ int64_t k;
+ int64_t v;
+ int r;
+ DBT key;
+ DBT val;
+ int i;
+
+ // printf("enter %s, iter = %d\n", __FUNCTION__, iter);
+ // printf("db1 = 0x%08lx, db2 = 0x%08lx, *txn = 0x%08lx, firstkey = %d, n = %d\n",
+ // (unsigned long) db1, (unsigned long) db2, (unsigned long) txn, firstkey, n);
+
+ fflush(stdout);
+
+ for (i = 0; i<n; i++) {
+ int64_t kk = firstkey+i;
+ v = generate_val(kk) + offset;
+ k = (kk<<32) + v;
+ //printf("I(%32lx,%32lx)\n", k, v);
+ dbt_init(&key, &k, sizeof(k));
+ dbt_init(&val, &v, sizeof(v));
+ if (db1) {
+ r = db1->put(db1, txn, &key, &val, 0);
+ CKERR(r);
+ }
+ if (db2) {
+ r = db2->put(db2, txn, &key, &val, 0);
+ CKERR(r);
+ }
+ }
+}
+
+
+static void UU()
+insert_n_broken(DB *db1, DB *db2, DB_TXN *txn, int firstkey, int n) {
+ insert_n(db1, db2, txn, firstkey, n, 2718);
+}
+
+
+static void UU()
+insert_n_fixed(DB *db1, DB *db2, DB_TXN *txn, int firstkey, int n) {
+ insert_n(db1, db2, txn, firstkey, n, 0);
+}
+
+
+// assert that correct values are in expected rows
+static void UU()
+verify_sequential_rows(DB* compare_db, int64_t firstkey, int64_t numkeys) {
+ //This does not lock the dbs/grab table locks.
+ //This means that you CANNOT CALL THIS while another thread is modifying the db.
+ //You CAN call it while a txn is open however.
+ DB_TXN *compare_txn;
+ int r, r1;
+
+ assert(numkeys >= 1);
+ r = env->txn_begin(env, NULL, &compare_txn, DB_READ_UNCOMMITTED);
+ CKERR(r);
+ DBC *c1;
+
+ r = compare_db->cursor(compare_db, compare_txn, &c1, 0);
+ CKERR(r);
+
+
+ DBT key1, val1;
+ DBT key2, val2;
+
+ int64_t k, v;
+
+ dbt_init_realloc(&key1);
+ dbt_init_realloc(&val1);
+
+ dbt_init(&key2, &k, sizeof(k));
+ dbt_init(&val2, &v, sizeof(v));
+
+ v = generate_val(firstkey);
+ k = (firstkey<<32) + v;
+ r1 = c1->c_get(c1, &key2, &val2, DB_SET);
+ CKERR(r1);
+
+ int64_t i;
+ for (i = 1; i<numkeys; i++) {
+ int64_t kk = firstkey+i;
+ v = generate_val(kk);
+ k = (kk<<32) + v;
+ r1 = c1->c_get(c1, &key1, &val1, DB_NEXT);
+ assert(r1==0);
+ assert(key1.size==8 && val1.size==8 && *(int64_t*)key1.data==k && *(int64_t*)val1.data==v);
+ }
+ // now verify that there are no rows after the last expected
+ r1 = c1->c_get(c1, &key1, &val1, DB_NEXT);
+ assert(r1 == DB_NOTFOUND);
+
+ c1->c_close(c1);
+ if (key1.data) toku_free(key1.data);
+ if (val1.data) toku_free(val1.data);
+ compare_txn->commit(compare_txn, 0);
+}
+
+
+
+static void UU()
+snapshot(DICTIONARY d, int do_checkpoint) {
+ if (do_checkpoint) {
+ int r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+ }
+ else {
+ db_shutdown(d);
+ db_startup(d, NULL);
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/create-datadir.cc b/storage/tokudb/PerconaFT/src/tests/create-datadir.cc
new file mode 100644
index 00000000000..4845a7dd576
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/create-datadir.cc
@@ -0,0 +1,118 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test data directories
+
+#include <sys/stat.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "a.db", NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "bdir/b.db", NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ CKERR(r); //Success, so need a new handle
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ char path[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(path, 2, TOKU_TEST_FILENAME, "bdir"), 0777); assert(r == 0);
+ r = db->open(db, NULL, "bdir/b.db", NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+
+ r = toku_os_mkdir(toku_path_join(path, 2, TOKU_TEST_FILENAME, "cdir"), 0777); assert(r == 0);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_data_dir(env, "cdir"); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "c.db", NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+const char *cmd;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ run_test();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/cursor-isolation.cc b/storage/tokudb/PerconaFT/src/tests/cursor-isolation.cc
new file mode 100644
index 00000000000..e748f957c3b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/cursor-isolation.cc
@@ -0,0 +1,136 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that flag settings for cursor isolation works
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ {
+ DB_TXN *txna;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ DBT key,val;
+ r = db->put(db, txna, dbt_init(&key, "a", 2), dbt_init(&val, "a", 2), 0); CKERR(r);
+
+ r = txna->commit(txna, 0); CKERR(r);
+ }
+
+ DB_TXN *txn_serializable, *txn_committed, *txn_uncommitted;
+ DBC* cursor = NULL;
+ r = env->txn_begin(env, NULL, &txn_serializable, DB_SERIALIZABLE); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn_committed, DB_READ_COMMITTED); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn_uncommitted, DB_READ_UNCOMMITTED); CKERR(r);
+
+
+ r = db->cursor(db, txn_serializable, &cursor, DB_SERIALIZABLE|DB_READ_COMMITTED); CKERR2(r, EINVAL);
+ r = db->cursor(db, txn_serializable, &cursor, DB_SERIALIZABLE|DB_READ_UNCOMMITTED); CKERR2(r, EINVAL);
+ r = db->cursor(db, txn_serializable, &cursor, DB_READ_UNCOMMITTED|DB_READ_COMMITTED); CKERR2(r, EINVAL);
+
+
+ r = db->cursor(db, txn_serializable, &cursor, 0); CKERR(r);
+ r = cursor->c_close(cursor); CKERR(r);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_serializable, &cursor, DB_SERIALIZABLE); CKERR(r);
+ r = cursor->c_close(cursor); CKERR(r);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_serializable, &cursor, DB_READ_COMMITTED); CKERR2(r, EINVAL);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_serializable, &cursor, DB_READ_UNCOMMITTED); CKERR2(r, EINVAL);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_committed, &cursor, 0); CKERR(r);
+ r = cursor->c_close(cursor); CKERR(r);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_committed, &cursor, DB_SERIALIZABLE); CKERR(r);
+ r = cursor->c_close(cursor); CKERR(r);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_committed, &cursor, DB_READ_COMMITTED); CKERR2(r, EINVAL);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_committed, &cursor, DB_READ_UNCOMMITTED); CKERR2(r, EINVAL);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_uncommitted, &cursor, 0); CKERR(r);
+ r = cursor->c_close(cursor); CKERR(r);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_uncommitted, &cursor, DB_SERIALIZABLE); CKERR(r);
+ r = cursor->c_close(cursor); CKERR(r);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_uncommitted, &cursor, DB_READ_COMMITTED); CKERR2(r, EINVAL);
+ cursor = NULL;
+
+ r = db->cursor(db, txn_uncommitted, &cursor, DB_READ_UNCOMMITTED); CKERR2(r, EINVAL);
+ cursor = NULL;
+
+
+
+
+ r = txn_serializable->commit(txn_serializable, 0); CKERR(r);
+ r = txn_committed->commit(txn_committed, 0); CKERR(r);
+ r = txn_uncommitted->commit(txn_uncommitted, 0); CKERR(r);
+
+
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/cursor-more-than-a-leaf-provdel.cc b/storage/tokudb/PerconaFT/src/tests/cursor-more-than-a-leaf-provdel.cc
new file mode 100644
index 00000000000..2f2b964ba2b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/cursor-more-than-a-leaf-provdel.cc
@@ -0,0 +1,145 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+DB_TXN *txn;
+
+const int num_insert = 25000;
+
+static void
+setup (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ r=env->set_redzone(env, 0); CKERR(r);
+ r=env->set_default_bt_compare(env, int_dbt_cmp); CKERR(r);
+ env->set_errfile(env, stderr);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int r;
+ r= db->close(db, 0); CKERR(r);
+ r= env->close(env, 0); CKERR(r);
+}
+
+static void
+doit (bool committed_provdels) {
+ DBT key,data;
+ DBC *dbc;
+ int r;
+ int i;
+ int j;
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (i = 0; i < num_insert; i++) {
+ j = (i<<1) + 37;
+ r=db->put(db, txn, dbt_init(&key, &i, sizeof(i)), dbt_init(&data, &j, sizeof(j)), 0);
+ }
+ r=txn->commit(txn, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->cursor(db, txn, &dbc, 0); CKERR(r);
+ for (i = 0; i < num_insert; i++) {
+ j = (i<<1) + 37;
+ r = dbc->c_get(dbc, &key, &data, DB_NEXT); CKERR(r);
+ assert(*(int*)key.data == i);
+ assert(*(int*)data.data == j);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); CKERR(r);
+ }
+ r = dbc->c_get(dbc, &key, &data, DB_NEXT); CKERR2(r, DB_NOTFOUND);
+ r = dbc->c_get(dbc, &key, &data, DB_FIRST); CKERR2(r, DB_NOTFOUND);
+ if (committed_provdels) {
+ r = dbc->c_close(dbc); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->cursor(db, txn, &dbc, 0); CKERR(r);
+ }
+ int ifirst, ilast, jfirst, jlast;
+ ilast=2*num_insert;
+ jlast=(ilast<<1)+37;
+ ifirst=-1*num_insert;
+ jfirst=(ifirst<<1)+37;
+ r=db->put(db, txn, dbt_init(&key, &ifirst, sizeof(ifirst)), dbt_init(&data, &jfirst, sizeof(jfirst)), 0);
+ CKERR(r);
+ r=db->put(db, txn, dbt_init(&key, &ilast, sizeof(ilast)), dbt_init(&data, &jlast, sizeof(jlast)), 0);
+ CKERR(r);
+
+ r = dbc->c_get(dbc, dbt_init(&key, NULL, 0), dbt_init(&data, NULL, 0), DB_FIRST); CKERR(r);
+ assert(*(int*)key.data == ifirst);
+ assert(*(int*)data.data == jfirst);
+ r = dbc->c_get(dbc, dbt_init(&key, NULL, 0), dbt_init(&data, NULL, 0), DB_NEXT); CKERR(r);
+ assert(*(int*)key.data == ilast);
+ assert(*(int*)data.data == jlast);
+ r = dbc->c_get(dbc, dbt_init(&key, NULL, 0), dbt_init(&data, NULL, 0), DB_LAST); CKERR(r);
+ assert(*(int*)key.data == ilast);
+ assert(*(int*)data.data == jlast);
+ r = dbc->c_get(dbc, dbt_init(&key, NULL, 0), dbt_init(&data, NULL, 0), DB_PREV); CKERR(r);
+ assert(*(int*)key.data == ifirst);
+ assert(*(int*)data.data == jfirst);
+ r = dbc->c_close(dbc); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+
+ setup();
+ doit(true);
+ test_shutdown();
+ setup();
+ doit(false);
+ test_shutdown();
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/cursor-set-del-rmw.cc b/storage/tokudb/PerconaFT/src/tests/cursor-set-del-rmw.cc
new file mode 100644
index 00000000000..5ebdf82b689
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/cursor-set-del-rmw.cc
@@ -0,0 +1,146 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// TODO
+
+static void test_del_rmw(DB_ENV *env, DB *db, uint32_t t1_flags, uint32_t t2_flags, uint32_t c1_flags, uint32_t c2_flags, int expect_r) {
+ int r;
+
+ {
+ DB_TXN *write_txn = NULL;
+ r = env->txn_begin(env, NULL, &write_txn, 0); assert_zero(r);
+ for (int i = 1; i <= 3; i++) {
+ int k = htonl(i); int v = i;
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v, sizeof v);
+ r = db->put(db, write_txn, &key, &val, 0); assert_zero(r);
+ }
+ r = write_txn->commit(write_txn, 0); assert_zero(r);
+ }
+
+ {
+ DB_TXN *txn1 = NULL;
+ r = env->txn_begin(env, NULL, &txn1, t1_flags); assert_zero(r);
+
+ DB_TXN *txn2 = NULL;
+ r = env->txn_begin(env, NULL, &txn2, t2_flags); assert_zero(r);
+
+ DBC *c1 = NULL;
+ r = db->cursor(db, txn1, &c1, c1_flags); assert_zero(r);
+
+ DBC *c2 = NULL;
+ r = db->cursor(db, txn2, &c2, c2_flags); assert_zero(r);
+
+ r = c1->c_set_bounds(c1, db->dbt_neg_infty(), db->dbt_pos_infty(), true, 0); assert_zero(r);
+
+ int k = htonl(2);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn1, &key, 0); assert_zero(r);
+
+ k = htonl(1);
+ DBT val; memset(&val, 0, sizeof val);
+ r = c2->c_get(c2, &key, &val, DB_SET); assert(r == expect_r);
+
+ r = c1->c_close(c1); assert_zero(r);
+ r = c2->c_close(c2); assert_zero(r);
+
+ r = txn1->commit(txn1, 0); assert_zero(r);
+ r = txn2->commit(txn2, 0); assert_zero(r);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+
+ const char *env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "rmwtest";
+
+ parse_args(argc, argv);
+
+ char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
+ r = system(rm_cmd); assert_zero(r);
+
+ r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ r = env->open(env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+ DB_TXN *create_txn = NULL;
+ r = env->txn_begin(env, NULL, &create_txn, 0); assert_zero(r);
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+ r = create_txn->commit(create_txn, 0); assert_zero(r);
+
+ // t1: prelock read, del(2)
+ // t2: set(1)
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_READ_UNCOMMITTED, 0, 0, 0);
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_READ_COMMITTED, 0, 0, 0);
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_TXN_SNAPSHOT, 0, 0, 0);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_SERIALIZABLE, 0, 0, DB_LOCK_NOTGRANTED);
+#else
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_SERIALIZABLE, 0, 0, 0);
+#endif
+
+ // t1: prelock write, del(2)
+ // t2: set(1)
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_READ_UNCOMMITTED, DB_RMW, 0, 0);
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_READ_COMMITTED, DB_RMW, 0, 0);
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_TXN_SNAPSHOT , DB_RMW, 0, 0);
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_SERIALIZABLE, DB_RMW, 0, DB_LOCK_NOTGRANTED);
+
+ // t1: prelock write, del(2)
+ // t2: rmw set(1)
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_READ_UNCOMMITTED, DB_RMW, DB_RMW, DB_LOCK_NOTGRANTED);
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_READ_COMMITTED, DB_RMW, DB_RMW, DB_LOCK_NOTGRANTED);
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_TXN_SNAPSHOT , DB_RMW, DB_RMW, DB_LOCK_NOTGRANTED);
+ test_del_rmw(env, db, DB_SERIALIZABLE, DB_SERIALIZABLE, DB_RMW, DB_RMW, DB_LOCK_NOTGRANTED);
+
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/cursor-set-range-rmw.cc b/storage/tokudb/PerconaFT/src/tests/cursor-set-range-rmw.cc
new file mode 100644
index 00000000000..6a19da73e44
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/cursor-set-range-rmw.cc
@@ -0,0 +1,160 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that the DB_RMW flag on cursor create grabs write locks for cursor set operations
+
+static void test_create_rmw(DB_ENV *env, DB *db, int k, uint32_t txn1_flags, uint32_t txn2_flags, int expect_r) {
+ int r;
+
+ DB_TXN *txn1 = NULL;
+ r = env->txn_begin(env, NULL, &txn1, 0); assert_zero(r);
+
+ DB_TXN *txn2 = NULL;
+ r = env->txn_begin(env, NULL, &txn2, 0); assert_zero(r);
+
+ DBC *c1 = NULL;
+ r = db->cursor(db, txn1, &c1, txn1_flags); assert_zero(r);
+
+ DBC *c2 = NULL;
+ r = db->cursor(db, txn2, &c2, txn2_flags); assert_zero(r);
+
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; memset(&val, 0, sizeof val);
+ r = c1->c_get(c1, &key, &val, DB_SET); assert_zero(r);
+
+ r = c2->c_get(c2, &key, &val, DB_SET); assert(r == expect_r);
+
+ r = c1->c_close(c1); assert_zero(r);
+ r = c2->c_close(c2); assert_zero(r);
+
+ r = txn1->commit(txn1, 0); assert_zero(r);
+ r = txn2->commit(txn2, 0); assert_zero(r);
+}
+
+// verify that the DB_RMW flag to the cursor set operations grabs write locks
+
+static void test_set_rmw(DB_ENV *env, DB *db, int k, uint32_t txn1_flags, uint32_t txn2_flags, int expect_r) {
+ int r;
+
+ DB_TXN *txn1 = NULL;
+ r = env->txn_begin(env, NULL, &txn1, 0); assert_zero(r);
+
+ DB_TXN *txn2 = NULL;
+ r = env->txn_begin(env, NULL, &txn2, 0); assert_zero(r);
+
+ DBC *c1 = NULL;
+ r = db->cursor(db, txn1, &c1, 0); assert_zero(r);
+
+ DBC *c2 = NULL;
+ r = db->cursor(db, txn2, &c2, 0); assert_zero(r);
+
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; memset(&val, 0, sizeof val);
+ r = c1->c_get(c1, &key, &val, DB_SET + txn1_flags); assert_zero(r);
+
+ r = c2->c_get(c2, &key, &val, DB_SET + txn2_flags); assert(r == expect_r);
+
+ r = c1->c_close(c1); assert_zero(r);
+ r = c2->c_close(c2); assert_zero(r);
+
+ r = txn1->commit(txn1, 0); assert_zero(r);
+ r = txn2->commit(txn2, 0); assert_zero(r);
+}
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+
+ const char *env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "rmwtest";
+
+ parse_args(argc, argv);
+
+ char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
+ r = system(rm_cmd); assert_zero(r);
+
+ r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ r = env->open(env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+ DB_TXN *create_txn = NULL;
+ r = env->txn_begin(env, NULL, &create_txn, 0); assert_zero(r);
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+ r = create_txn->commit(create_txn, 0); assert_zero(r);
+
+ DB_TXN *write_txn = NULL;
+ r = env->txn_begin(env, NULL, &write_txn, 0); assert_zero(r);
+
+ int k = htonl(42); int v = 42;
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v, sizeof v);
+ r = db->put(db, write_txn, &key, &val, DB_NOOVERWRITE); assert_zero(r);
+ r = write_txn->commit(write_txn, 0); assert_zero(r);
+
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ test_set_rmw(env, db, k, 0, 0, DB_LOCK_NOTGRANTED);
+#else
+ test_set_rmw(env, db, k, 0, 0, 0);
+#endif
+ test_set_rmw(env, db, k, 0, DB_RMW, DB_LOCK_NOTGRANTED);
+ test_set_rmw(env, db, k, DB_RMW, 0, DB_LOCK_NOTGRANTED);
+ test_set_rmw(env, db, k, DB_RMW, DB_RMW, DB_LOCK_NOTGRANTED);
+
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ test_create_rmw(env, db, k, 0, 0, DB_LOCK_NOTGRANTED);
+#else
+ test_create_rmw(env, db, k, 0, 0, 0);
+#endif
+ test_create_rmw(env, db, k, 0, DB_RMW, DB_LOCK_NOTGRANTED);
+ test_create_rmw(env, db, k, DB_RMW, 0, DB_LOCK_NOTGRANTED);
+ test_create_rmw(env, db, k, DB_RMW, DB_RMW, DB_LOCK_NOTGRANTED);
+
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/cursor-step-over-delete.cc b/storage/tokudb/PerconaFT/src/tests/cursor-step-over-delete.cc
new file mode 100644
index 00000000000..eac7d3d7519
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/cursor-step-over-delete.cc
@@ -0,0 +1,108 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+DB_TXN *txn;
+
+static void
+test_setup (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int r;
+ r= db->close(db, 0); CKERR(r);
+ r= env->close(env, 0); CKERR(r);
+}
+
+static void
+doit (void) {
+ DBT key,data;
+ int r;
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->put(db, txn, dbt_init(&key, "a", 2), dbt_init(&data, "a", 2), 0);
+ r=db->put(db, txn, dbt_init(&key, "b", 2), dbt_init(&data, "b", 2), 0);
+ r=db->put(db, txn, dbt_init(&key, "c", 2), dbt_init(&data, "c", 2), 0);
+ r=txn->commit(txn, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->del(db, txn, dbt_init(&key, "b", 2), 0); assert(r==0);
+ r=txn->commit(txn, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ DBC *dbc;
+ r = db->cursor(db, txn, &dbc, 0); assert(r==0);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ r = dbc->c_get(dbc, &key, &data, DB_FIRST); assert(r==0);
+ assert(strcmp((char*)key.data, "a")==0);
+ assert(strcmp((char*)data.data, "a")==0);
+ r = dbc->c_get(dbc, &key, &data, DB_NEXT); assert(r==0);
+ assert(strcmp((char*)key.data, "c")==0);
+ assert(strcmp((char*)data.data, "c")==0);
+ r = dbc->c_close(dbc); assert(r==0);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+
+ test_setup();
+ doit();
+ test_shutdown();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/db-put-simple-deadlock-threads.cc b/storage/tokudb/PerconaFT/src/tests/db-put-simple-deadlock-threads.cc
new file mode 100644
index 00000000000..ada46bd7516
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/db-put-simple-deadlock-threads.cc
@@ -0,0 +1,241 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test demonstrates that the lock manager can detect a simple deadlock with 2 transactions on 2 threads
+// the threads do:
+// T(a) put 0, grabs write lock on 0
+// T(b) put N-1, grabs write lock on N-1
+// T(a) put N-1, try's to grab write lock on N-1, should return lock not granted
+// T(b) put 0, try's to grab write lock on 0, should return deadlock
+// T(b) abort
+// T(a) gets lock W(N-1)
+// T(A) commit
+
+#include "test.h"
+#include "toku_pthread.h"
+
+struct test_seq {
+ int state;
+ toku_mutex_t lock;
+ toku_cond_t cv;
+};
+
+static void test_seq_init(struct test_seq *seq) {
+ seq->state = 0;
+ toku_mutex_init(&seq->lock, NULL);
+ toku_cond_init(&seq->cv, NULL);
+}
+
+static void test_seq_destroy(struct test_seq *seq) {
+ toku_mutex_destroy(&seq->lock);
+ toku_cond_destroy(&seq->cv);
+}
+
+static void test_seq_sleep(struct test_seq *seq, int new_state) {
+ toku_mutex_lock(&seq->lock);
+ while (seq->state != new_state) {
+ toku_cond_wait(&seq->cv, &seq->lock);
+ }
+ toku_mutex_unlock(&seq->lock);
+}
+
+static void test_seq_next_state(struct test_seq *seq) {
+ toku_mutex_lock(&seq->lock);
+ seq->state++;
+ toku_cond_broadcast(&seq->cv);
+ toku_mutex_unlock(&seq->lock);
+}
+
+static void insert_row(DB *db, DB_TXN *txn, int k, int v, int expect_r) {
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT value; dbt_init(&value, &v, sizeof v);
+ int r = db->put(db, txn, &key, &value, 0); assert(r == expect_r);
+}
+
+struct run_txn_b_arg {
+ struct test_seq *test_seq;
+ DB_TXN *txn_b;
+ DB *db;
+ int n;
+};
+
+static void *run_txn_b(void *arg) {
+ struct run_txn_b_arg *b_arg = (struct run_txn_b_arg *) arg;
+ struct test_seq *test_seq = b_arg->test_seq;
+ DB_TXN *txn_b = b_arg->txn_b;
+ DB *db = b_arg->db;
+ int n = b_arg->n;
+
+ test_seq_sleep(test_seq, 1);
+ insert_row(db, txn_b, htonl(n-1), n-1, 0);
+ test_seq_next_state(test_seq);
+
+ test_seq_sleep(test_seq, 3);
+ insert_row(db, txn_b, htonl(0), 0, DB_LOCK_NOTGRANTED);
+ test_seq_next_state(test_seq);
+
+ test_seq_sleep(test_seq, 5);
+ int r = txn_b->abort(txn_b); assert(r == 0);
+
+ return arg;
+}
+
+static void simple_deadlock(DB_ENV *db_env, DB *db, int do_txn, int n) {
+ int r;
+
+ DB_TXN *txn_init = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txn_init, 0); assert(r == 0);
+ }
+
+ for (int k = 0; k < n; k++) {
+ insert_row(db, txn_init, htonl(k), k, 0);
+ }
+
+ if (do_txn) {
+ r = txn_init->commit(txn_init, 0); assert(r == 0);
+ }
+
+ uint32_t txn_flags = 0;
+
+ DB_TXN *txn_a = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txn_a, txn_flags); assert(r == 0);
+ }
+
+ DB_TXN *txn_b = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txn_b, txn_flags); assert(r == 0);
+ }
+
+ struct test_seq test_seq; ZERO_STRUCT(test_seq); test_seq_init(&test_seq);
+
+ toku_pthread_t tid;
+ struct run_txn_b_arg arg = { &test_seq, txn_b, db, n};
+ r = toku_pthread_create(&tid, NULL, run_txn_b, &arg);
+
+ test_seq_sleep(&test_seq, 0);
+ insert_row(db, txn_a, htonl(0), 0, 0);
+ test_seq_next_state(&test_seq);
+
+ test_seq_sleep(&test_seq, 2);
+ insert_row(db, txn_a, htonl(n-1), n-1, DB_LOCK_NOTGRANTED);
+ test_seq_next_state(&test_seq);
+
+ test_seq_sleep(&test_seq, 4);
+ if (do_txn) {
+ r = txn_a->abort(txn_a); assert(r == 0);
+ }
+ test_seq_next_state(&test_seq);
+
+ void *ret = NULL;
+ r = toku_pthread_join(tid, &ret); assert(r == 0);
+
+ test_seq_destroy(&test_seq);
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ int do_txn = 1;
+ int nrows = 1000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "simple_deadlock";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "-n") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ if (!do_txn)
+ db_env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 0, nullptr); assert(r == 0); // no wait
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ DB_TXN *create_txn = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert(r == 0);
+ }
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ if (do_txn) {
+ r = create_txn->commit(create_txn, 0); assert(r == 0);
+ }
+
+ // run test
+ simple_deadlock(db_env, db, do_txn, nrows);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/db-put-simple-deadlock.cc b/storage/tokudb/PerconaFT/src/tests/db-put-simple-deadlock.cc
new file mode 100644
index 00000000000..c208a4629c3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/db-put-simple-deadlock.cc
@@ -0,0 +1,158 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test demonstrates that a simple deadlock with 2 transactions on a single thread works with tokudb
+
+#include "test.h"
+
+static void insert_row(DB *db, DB_TXN *txn, int k, int v, int expect_r) {
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT value; dbt_init(&value, &v, sizeof v);
+ int r = db->put(db, txn, &key, &value, 0); assert(r == expect_r);
+}
+
+static void simple_deadlock(DB_ENV *db_env, DB *db, int do_txn, int n) {
+ int r;
+
+ DB_TXN *txn_init = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txn_init, 0); assert(r == 0);
+ }
+
+ for (int k = 0; k < n; k++) {
+ insert_row(db, txn_init, htonl(k), k, 0);
+ }
+
+ if (do_txn) {
+ r = txn_init->commit(txn_init, 0); assert(r == 0);
+ }
+
+ uint32_t txn_flags = 0;
+
+ DB_TXN *txn_a = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txn_a, txn_flags); assert(r == 0);
+ }
+
+ DB_TXN *txn_b = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txn_b, txn_flags); assert(r == 0);
+ }
+
+ insert_row(db, txn_a, htonl(0), 0, 0);
+
+ insert_row(db, txn_b, htonl(n-1), n-1, 0);
+
+ insert_row(db, txn_a, htonl(n-1), n-1, DB_LOCK_NOTGRANTED);
+
+ insert_row(db, txn_b, htonl(0), 0, DB_LOCK_NOTGRANTED);
+
+ if (do_txn) {
+ r = txn_a->commit(txn_a, 0); assert(r == 0);
+ r = txn_b->commit(txn_b, 0); assert(r == 0);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ int do_txn = 1;
+ int nrows = 1000;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "simple_deadlock";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "-n") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ if (!do_txn)
+ db_env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ DB_TXN *create_txn = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert(r == 0);
+ }
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ if (do_txn) {
+ r = create_txn->commit(create_txn, 0); assert(r == 0);
+ }
+
+ // run test
+ simple_deadlock(db_env, db, do_txn, nrows);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/db-put-simple-lockwait.cc b/storage/tokudb/PerconaFT/src/tests/db-put-simple-lockwait.cc
new file mode 100644
index 00000000000..365ea39eced
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/db-put-simple-lockwait.cc
@@ -0,0 +1,189 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// T(a) put 0
+// T(b) put 0, should block
+// T(c) put 0, should block
+// T(a) commit
+// T(b) put 0 succeeds
+// T(b) commit
+// T(c) put 0 succeeds
+
+#include "test.h"
+#include "toku_pthread.h"
+
+static void insert_row(DB *db, DB_TXN *txn, int k, int v, int expect_r) {
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT value; dbt_init(&value, &v, sizeof v);
+ int r = db->put(db, txn, &key, &value, 0); assert(r == expect_r);
+}
+
+struct insert_one_arg {
+ DB_TXN *txn;
+ DB *db;
+};
+
+static void *insert_one(void *arg) {
+ struct insert_one_arg *f_arg = (struct insert_one_arg *) arg;
+ DB_TXN *txn = f_arg->txn;
+ DB *db = f_arg->db;
+
+ insert_row(db, txn, htonl(0), 0, 0);
+ if (txn) {
+ int r = txn->commit(txn, 0); assert(r == 0);
+ }
+ return arg;
+}
+
+static void simple_lockwait(DB_ENV *db_env, DB *db, int do_txn, int nrows, int ntxns) {
+ int r;
+
+ DB_TXN *txn_init = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txn_init, 0); assert(r == 0);
+ }
+ for (int k = 0; k < nrows; k++) {
+ insert_row(db, txn_init, htonl(k), k, 0);
+ }
+ if (do_txn) {
+ r = txn_init->commit(txn_init, 0); assert(r == 0);
+ }
+
+ DB_TXN *txns[ntxns];
+ for (int i = 0; i < ntxns; i++) {
+ txns[i] = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txns[i], 0); assert(r == 0);
+ }
+ }
+
+ insert_row(db, txns[0], htonl(0), 0, 0);
+
+ toku_pthread_t tids[ntxns];
+ for (int i = 1 ; i < ntxns; i++) {
+ struct insert_one_arg *XMALLOC(arg);
+ *arg = (struct insert_one_arg) { txns[i], db};
+ r = toku_pthread_create(&tids[i], NULL, insert_one, arg);
+ }
+
+ sleep(10);
+ if (do_txn) {
+ r = txns[0]->commit(txns[0], 0); assert(r == 0);
+ }
+
+ for (int i = 1; i < ntxns; i++) {
+ void *ret = NULL;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0); toku_free(ret);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ int do_txn = 1;
+ int nrows = 1000;
+ int ntxns = 2;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "simple_lockwait";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--ntxns") == 0 && i+1 < argc) {
+ ntxns = atoi(argv[++i]);
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ if (!do_txn)
+ db_env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ DB_TXN *create_txn = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert(r == 0);
+ }
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ if (do_txn) {
+ r = create_txn->commit(create_txn, 0); assert(r == 0);
+ }
+
+ // run test
+ simple_lockwait(db_env, db, do_txn, nrows, ntxns);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/db-put-update-deadlock.cc b/storage/tokudb/PerconaFT/src/tests/db-put-update-deadlock.cc
new file mode 100644
index 00000000000..3660f11d1bd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/db-put-update-deadlock.cc
@@ -0,0 +1,237 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// for all i: T(i) reads 0, gets a read lock on 0
+// for all i: T(i) writes 0, enters a deadlock
+// tokudb detects deadlock on the fly
+// --poll runs the deadlock detector until all the txns are resolved
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <portability/toku_atomic.h>
+
+static void write_row(DB *db, DB_TXN *txn, int k, int v, int expect_r) {
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT value; dbt_init(&value, &v, sizeof v);
+ int r = db->put(db, txn, &key, &value, 0); assert(r == expect_r);
+}
+
+static void read_row(DB *db, DB_TXN *txn, int k, int expect_r) {
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT value; dbt_init_malloc(&value);
+ int r = db->get(db, txn, &key, &value, 0); assert(r == expect_r);
+ toku_free(value.data);
+}
+
+static volatile int n_txns;
+
+struct write_one_arg {
+ DB_TXN *txn;
+ DB *db;
+ int k;
+ int v;
+};
+
+static void *write_one_f(void *arg) {
+ struct write_one_arg *f_arg = (struct write_one_arg *) arg;
+ DB_TXN *txn = f_arg->txn;
+ DB *db = f_arg->db;
+ int k = f_arg->k;
+ int v = f_arg->v;
+
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT value; dbt_init(&value, &v, sizeof v);
+ int r = db->put(db, txn, &key, &value, 0);
+ if (verbose)
+ printf("%s %p %d\n", __FUNCTION__, arg, r);
+ assert(r == 0 || r == DB_LOCK_DEADLOCK);
+ if (r == 0) {
+ r = txn->commit(txn, 0); assert(r == 0);
+ } else {
+ r = txn->abort(txn); assert(r == 0);
+ }
+ (void) toku_sync_fetch_and_sub(&n_txns, 1);
+
+ return arg;
+}
+
+static void update_deadlock(DB_ENV *db_env, DB *db, int do_txn, int nrows, int ntxns, int poll_deadlock UU()) {
+ int r;
+
+ // populate the initial tree
+ DB_TXN *txn_init = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txn_init, 0); assert(r == 0);
+ }
+ for (int k = 0; k < nrows; k++) {
+ write_row(db, txn_init, htonl(k), k, 0);
+ }
+ if (do_txn) {
+ r = txn_init->commit(txn_init, 0); assert(r == 0);
+ }
+
+ // create the transactions
+ n_txns = ntxns;
+ DB_TXN *txns[ntxns];
+ for (int i = 0; i < ntxns; i++) {
+ txns[i] = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &txns[i], 0); assert(r == 0);
+ }
+ }
+
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ // spice this test up a bit when reads locks are not shared.
+ // test that a dining philosopher's style deadlock is detected
+ // by having each txn take a distinct read lock, and then request
+ // a write lock on the value "next" to it (i + 1 mod ntxns)
+
+ // get read locks
+ for (int i = 0; i < ntxns; i++) {
+ read_row(db, txns[i], htonl(i), 0);
+ }
+
+ // get write locks
+ toku_pthread_t tids[ntxns];
+ for (int i = 0 ; i < ntxns; i++) {
+ struct write_one_arg *XMALLOC(arg);
+ *arg = (struct write_one_arg) { txns[i], db, (int) htonl((i + 1) % ntxns), 0};
+ r = toku_pthread_create(&tids[i], NULL, write_one_f, arg);
+ }
+#else
+ // get read locks
+ for (int i = 0; i < ntxns; i++) {
+ read_row(db, txns[i], htonl(0), 0);
+ }
+
+ // get write locks
+ toku_pthread_t tids[ntxns];
+ for (int i = 0 ; i < ntxns; i++) {
+ struct write_one_arg *XMALLOC(arg);
+ *arg = (struct write_one_arg) { txns[i], db, (int) htonl(0), 0};
+ r = toku_pthread_create(&tids[i], NULL, write_one_f, arg);
+ }
+#endif
+
+ // cleanup
+ for (int i = 0; i < ntxns; i++) {
+ void *ret = NULL;
+ r = toku_pthread_join(tids[i], &ret); assert(r == 0); toku_free(ret);
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+ int do_txn = 1;
+ int nrows = 1000;
+ int ntxns = 2;
+ int poll_deadlock = 0;
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "simple_deadlock";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_THREAD;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--ntxns") == 0 && i+1 < argc) {
+ ntxns = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--poll") == 0) {
+ poll_deadlock = 1;
+ continue;
+ }
+ assert(0);
+ }
+
+ // setup env
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert(r == 0);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert(r == 0);
+
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert(r == 0);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert(r == 0);
+ }
+ if (!do_txn)
+ db_env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ r = db_env->set_lock_timeout(db_env, 30 * 1000, nullptr); assert(r == 0);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert(r == 0);
+ DB_TXN *create_txn = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert(r == 0);
+ }
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert(r == 0);
+ if (do_txn) {
+ r = create_txn->commit(create_txn, 0); assert(r == 0);
+ }
+
+ // run test
+ update_deadlock(db_env, db, do_txn, nrows, ntxns, poll_deadlock);
+
+ // close env
+ r = db->close(db, 0); assert(r == 0); db = NULL;
+ r = db_env->close(db_env, 0); assert(r == 0); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/dbremove-nofile-limit.cc b/storage/tokudb/PerconaFT/src/tests/dbremove-nofile-limit.cc
new file mode 100644
index 00000000000..1d656efa025
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/dbremove-nofile-limit.cc
@@ -0,0 +1,125 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// This test verifies that the env->dbremove function returns an error rather than
+// crash when the NOFILE resource limit is exceeded.
+
+#include "test.h"
+#include <db.h>
+#include <sys/resource.h>
+
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static void test_dbremove() {
+ int r;
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ char fname[32];
+ sprintf(fname, "db%d", 0);
+ r = db->open(db, nullptr, fname, nullptr, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, nullptr, &txn, 0); CKERR(r);
+
+ struct rlimit current_limit;
+ r = getrlimit(RLIMIT_NOFILE, &current_limit);
+ assert(r == 0);
+
+ struct rlimit new_limit = current_limit;
+ new_limit.rlim_cur = 0;
+ r = setrlimit(RLIMIT_NOFILE, &new_limit);
+ assert(r == 0);
+
+ r = env->dbremove(env, txn, fname, nullptr, 0);
+ CKERR2(r, EMFILE);
+
+ r = setrlimit(RLIMIT_NOFILE, &current_limit);
+ assert(r == 0);
+
+ r = env->dbremove(env, txn, fname, nullptr, 0);
+ CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: %s -h -v -q\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ test_dbremove();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/del-multiple-huge-primary-row.cc b/storage/tokudb/PerconaFT/src/tests/del-multiple-huge-primary-row.cc
new file mode 100644
index 00000000000..661050538f8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/del-multiple-huge-primary-row.cc
@@ -0,0 +1,240 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that del_multiple logs individual delete log entries in the recovery log when
+// the sum of the log sizes of the individual deletes.
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ toku_dbt_array_resize(dest_keys, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_keys; (void) src_key; (void) src_data;
+ assert(src_db == NULL);
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_data->size / sizeof (int));
+ int *pri_data = (int *) src_data->data;
+
+ assert(dest_key->flags == 0);
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+
+ return 0;
+}
+
+static void
+verify_locked(DB_ENV *env, DB *db, int k) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn->abort(txn); assert_zero(r);
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static inline int
+max(int a, int b) {
+ return a < b ? b : a;
+}
+
+static void
+verify_del_multiple(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ int r;
+ DB_TXN *deltxn = NULL;
+ r = env->txn_begin(env, NULL, &deltxn, 0); assert_zero(r);
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ int v[max(ndbs,1024)]; get_data(v, i, ndbs);
+ DBT pri_data; dbt_init(&pri_data, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_del_multiple_test_no_array(env, NULL, deltxn, &pri_key, &pri_data, ndbs, db, keys, flags); assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_locked(env, db[dbnum], get_key(i, dbnum));
+ }
+ r = deltxn->commit(deltxn, 0); assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_empty(env, db[dbnum]);
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[max(ndbs, 1024)]; memset(v, 0, sizeof v); get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, dbnum);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ verify_del_multiple(env, db, ndbs, nrows);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 2;
+ int nrows = 2;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/del-multiple-srcdb.cc b/storage/tokudb/PerconaFT/src/tests/del-multiple-srcdb.cc
new file mode 100644
index 00000000000..0258039047c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/del-multiple-srcdb.cc
@@ -0,0 +1,235 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that del_multiple deletes the correct key from N dictionaries
+// verify that del_multiple locks the correct key for N dictionaries
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ toku_dbt_array_resize(dest_keys, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_keys; (void) src_key; (void) src_data;
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_data->size / sizeof (int));
+
+ int *pri_data = (int *) src_data->data;
+
+ assert(dest_key->flags == 0);
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+
+ return 0;
+}
+
+static void
+verify_locked(DB_ENV *env, DB *db, int k) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn->abort(txn); assert_zero(r);
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_del_multiple(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ int r;
+ DB_TXN *deltxn = NULL;
+ r = env->txn_begin(env, NULL, &deltxn, 0); assert_zero(r);
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_data; dbt_init(&pri_data, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_del_multiple_test_no_array(env, ndbs > 0 ? db[0] : NULL, deltxn, &pri_key, &pri_data, ndbs, db, keys, flags); assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_locked(env, db[dbnum], get_key(i, dbnum));
+ }
+ r = deltxn->commit(deltxn, 0); assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_empty(env, db[dbnum]);
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, dbnum);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ verify_del_multiple(env, db, ndbs, nrows);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 2;
+ int nrows = 2;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/del-multiple.cc b/storage/tokudb/PerconaFT/src/tests/del-multiple.cc
new file mode 100644
index 00000000000..db9f1b7b825
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/del-multiple.cc
@@ -0,0 +1,236 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that del_multiple deletes the correct key from N dictionaries
+// verify that del_multiple locks the correct key for N dictionaries
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ toku_dbt_array_resize(dest_keys, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_keys; (void) src_key; (void) src_data;
+ assert(src_db == NULL);
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_data->size / sizeof (int));
+
+ int *pri_data = (int *) src_data->data;
+
+ assert(dest_key->flags == 0);
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+
+ return 0;
+}
+
+static void
+verify_locked(DB_ENV *env, DB *db, int k) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn->abort(txn); assert_zero(r);
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_del_multiple(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ int r;
+ DB_TXN *deltxn = NULL;
+ r = env->txn_begin(env, NULL, &deltxn, 0); assert_zero(r);
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_data; dbt_init(&pri_data, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_del_multiple_test_no_array(env, NULL, deltxn, &pri_key, &pri_data, ndbs, db, keys, flags); assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_locked(env, db[dbnum], get_key(i, dbnum));
+ }
+ r = deltxn->commit(deltxn, 0); assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_empty(env, db[dbnum]);
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, dbnum);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ verify_del_multiple(env, db, ndbs, nrows);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 2;
+ int nrows = 2;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/del-simple.cc b/storage/tokudb/PerconaFT/src/tests/del-simple.cc
new file mode 100644
index 00000000000..20b8742f36a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/del-simple.cc
@@ -0,0 +1,152 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that deletes on single dictionaries delete the correct key and create the correct lock
+
+static void
+verify_locked(DB_ENV *env, DB *db, int k) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn->abort(txn); assert_zero(r);
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_del(DB_ENV *env, DB *db, int nrows) {
+ int r;
+ DB_TXN *deltxn = NULL;
+ r = env->txn_begin(env, NULL, &deltxn, 0); assert_zero(r);
+ for (int i = 0; i < nrows; i++) {
+ int k = htonl(i);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, deltxn, &key, DB_DELETE_ANY); assert_zero(r);
+ verify_locked(env, db, k);
+ }
+ r = deltxn->commit(deltxn, 0); assert_zero(r);
+ verify_empty(env, db);
+}
+
+static void
+test_del(int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+
+ r = db->open(db, NULL, "test.db", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = htonl(i);
+ int v = i;
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v, sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+
+ verify_del(env, db, nrows);
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int nrows = 2;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ test_del(nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/directory_lock.cc b/storage/tokudb/PerconaFT/src/tests/directory_lock.cc
new file mode 100644
index 00000000000..f040e680903
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/directory_lock.cc
@@ -0,0 +1,390 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that isolation works right for subtransactions.
+// In particular, check to see what happens if a subtransaction has different isolation level from its parent.
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+
+static int
+put_multiple_callback(DB *dest_db UU(), DB *src_db UU(), DBT_ARRAY *dest_keys UU(), DBT_ARRAY *dest_vals UU(), const DBT *src_key UU(), const DBT *src_val UU()) {
+ return 0;
+}
+
+static int
+del_multiple_callback(DB *dest_db UU(), DB *src_db UU(), DBT_ARRAY *dest_keys UU(), const DBT *src_key UU(), const DBT *src_val UU()) {
+ return 0;
+}
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *UU(old_val), const DBT *UU(extra),
+ void UU((*set_val)(const DBT *new_val,
+ void *set_extra)),
+ void *UU(set_extra)) {
+ return 0;
+}
+
+static void verify_shared_ops_fail(DB_ENV* env, DB* db) {
+ int r;
+ DB_TXN* txn = NULL;
+ uint32_t flags = 0;
+ DBT key,val;
+ DBT in_key,in_val;
+ uint32_t in_key_data, in_val_data = 0;
+ memset(&in_key, 0, sizeof(in_key));
+ memset(&in_val, 0, sizeof(in_val));
+ in_key.size = sizeof(in_key_data);
+ in_val.size = sizeof(in_val_data);
+ in_key.data = &in_key_data;
+ in_val.data = &in_val_data;
+ in_key.flags = DB_DBT_USERMEM;
+ in_val.flags = DB_DBT_USERMEM;
+ in_key.ulen = sizeof(in_key_data);
+ in_val.ulen = sizeof(in_val_data);
+ DBT in_keys[2];
+ memset(&in_keys, 0, sizeof(in_keys));
+ dbt_init(&key, "a", 2);
+ dbt_init(&val, "a", 2);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = db->put(
+ db,
+ txn,
+ &key,
+ &val,
+ 0
+ );
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = db->del(
+ db,
+ txn,
+ &key,
+ DB_DELETE_ANY
+ );
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env_put_multiple_test_no_array(
+ env, db, txn,
+ &key, &val,
+ 1, &db, &in_key, &in_val, &flags);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env_put_multiple_test_no_array(
+ env, NULL, txn,
+ &key, &val,
+ 1, &db, &in_key, &in_val, &flags);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ flags = DB_DELETE_ANY;
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env_del_multiple_test_no_array(
+ env, db, txn,
+ &key, &val,
+ 1, &db, &in_key, &flags);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env_del_multiple_test_no_array(
+ env, NULL, txn,
+ &key, &val,
+ 1, &db, &in_key, &flags);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ flags = 0;
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env_update_multiple_test_no_array(
+ env, NULL, txn,
+ &key, &val,
+ &key, &val,
+ 1, &db, &flags,
+ 2, in_keys,
+ 1, &in_val);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env_update_multiple_test_no_array(
+ env, db, txn,
+ &key, &val,
+ &key, &val,
+ 1, &db, &flags,
+ 2, in_keys,
+ 1, &in_val);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+
+ DBT extra_up;
+ dbt_init(&extra_up, NULL, 0);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = db->update(
+ db,
+ txn,
+ &key,
+ &extra_up,
+ 0
+ );
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = db->update_broadcast(db, txn, &extra_up, 0);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = db->update_broadcast(db, txn, &extra_up, DB_IS_RESETTING_OP);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = txn->commit(txn,0); CKERR(r);
+
+}
+
+static void verify_excl_ops_fail(DB_ENV* env, const char* name) {
+ DB_TXN* txn = NULL;
+ int r;
+ DBT extra_up;
+ dbt_init(&extra_up, NULL, 0);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env->dbrename(env, txn, name, NULL, "asdf.db", 0);
+ CKERR2(r, EINVAL);
+ r = txn->commit(txn,0); CKERR(r);
+
+}
+
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ DBT in_key,in_val;
+ uint32_t in_key_data = 123456;
+ uint32_t in_val_data = 654321;
+ memset(&in_key, 0, sizeof(in_key));
+ memset(&in_val, 0, sizeof(in_val));
+ in_key.size = sizeof(in_key_data);
+ in_val.size = sizeof(in_val_data);
+ in_key.data = &in_key_data;
+ in_val.data = &in_val_data;
+ in_key.flags = DB_DBT_USERMEM;
+ in_val.flags = DB_DBT_USERMEM;
+ in_key.ulen = sizeof(in_key_data);
+ in_val.ulen = sizeof(in_val_data);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB_LOADER* loader = NULL;
+ uint32_t put_flags = 0;
+ uint32_t dbt_flags = 0;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_callback);
+ CKERR(r);
+ r = env->set_generate_row_callback_for_del(env, del_multiple_callback);
+ CKERR(r);
+ env->set_update(env, update_fun);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB* db;
+ DB* db2;
+
+ DB_TXN* txna = NULL;
+ DB_TXN* txnb = NULL;
+
+
+ //
+ // transactionally create dictionary
+ //
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = db_create(&db2, env, 0); CKERR(r);
+ r = db2->open(db2, txna, "foo2.db", NULL, DB_BTREE, DB_CREATE|DB_IS_HOT_INDEX, 0666); CKERR(r);
+ verify_excl_ops_fail(env, "foo2.db");
+ r = txna->commit(txna, 0); CKERR(r);
+
+
+ //
+ // transactionally create dictionary
+ //
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = txna->commit(txna, 0); CKERR(r);
+
+ //
+ // create loader
+ //
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = env->create_loader(env, txna, &loader, NULL, 1, &db, &put_flags, &dbt_flags, 0); CKERR(r);
+ verify_shared_ops_fail(env,db);
+ r = loader->abort(loader); CKERR(r);
+ loader=NULL;
+ r = txna->commit(txna, 0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnb, 0); CKERR(r);
+ DBT key,val;
+ dbt_init(&key, "a", 2);
+ dbt_init(&val, "a", 2);
+ r = db->put(db, txna, &key, &val, 0); CKERR(r);
+ dbt_init(&key, "b", 2);
+ dbt_init(&val, "b", 2);
+ r = db->put(db, txnb, &key, &val, 0); CKERR(r);
+ verify_excl_ops_fail(env,"foo.db");
+ r = txna->abort(txna); CKERR(r);
+ r = txnb->abort(txnb); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnb, 0); CKERR(r);
+ dbt_init(&key, "a", 2);
+ r = db->del(db, txna, &key, DB_DELETE_ANY); CKERR(r);
+ dbt_init(&key, "b", 2);
+ r = db->del(db, txnb, &key, DB_DELETE_ANY); CKERR(r);
+ verify_excl_ops_fail(env,"foo.db");
+ r = txna->abort(txna); CKERR(r);
+ r = txnb->abort(txnb); CKERR(r);
+
+
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnb, 0); CKERR(r);
+ dbt_init(&key, "a", 2);
+ r = db->update(db, txna, &key, &val, 0); CKERR(r);
+ dbt_init(&key, "b", 2);
+ r = db->update(db, txnb, &key, &val, 0); CKERR(r);
+ verify_excl_ops_fail(env,"foo.db");
+ r = txna->abort(txna); CKERR(r);
+ r = txnb->abort(txnb); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = db->update_broadcast(db, txna, &val, 0); CKERR(r);
+ verify_excl_ops_fail(env,"foo.db");
+ r = txna->abort(txna); CKERR(r);
+
+ uint32_t flags = 0;
+
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnb, 0); CKERR(r);
+ dbt_init(&key, "a", 2);
+ dbt_init(&val, "a", 2);
+ env_put_multiple_test_no_array(
+ env, NULL, txna,
+ &key, &val,
+ 1, &db, &in_key, &in_val, &flags);
+ CKERR(r);
+ dbt_init(&key, "b", 2);
+ dbt_init(&val, "b", 2);
+ env_put_multiple_test_no_array(
+ env, NULL, txnb,
+ &key, &val,
+ 1, &db, &in_key, &in_val, &flags);
+ CKERR(r);
+ verify_excl_ops_fail(env,"foo.db");
+ r = txna->abort(txna); CKERR(r);
+ r = txnb->abort(txnb); CKERR(r);
+
+ flags = DB_DELETE_ANY;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnb, 0); CKERR(r);
+ dbt_init(&key, "a", 2);
+ dbt_init(&val, "a", 2);
+ env_del_multiple_test_no_array(
+ env, NULL, txna,
+ &key, &val,
+ 1, &db, &in_key, &flags);
+ CKERR(r);
+ dbt_init(&key, "b", 2);
+ dbt_init(&val, "b", 2);
+ env_del_multiple_test_no_array(
+ env, db, txnb,
+ &key, &val,
+ 1, &db, &in_key, &flags);
+ CKERR(r);
+ verify_excl_ops_fail(env,"foo.db");
+ r = txna->abort(txna); CKERR(r);
+ r = txnb->abort(txnb); CKERR(r);
+
+ flags = 0;
+ DBT in_keys[2];
+ memset(&in_keys, 0, sizeof(in_keys));
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnb, 0); CKERR(r);
+ dbt_init(&key, "a", 2);
+ dbt_init(&val, "a", 2);
+ env_update_multiple_test_no_array(
+ env, NULL, txna,
+ &key, &val,
+ &key, &val,
+ 1, &db, &flags,
+ 2, in_keys,
+ 1, &in_val);
+ CKERR(r);
+ dbt_init(&key, "b", 2);
+ dbt_init(&val, "b", 2);
+ env_update_multiple_test_no_array(
+ env, db, txnb,
+ &key, &val,
+ &key, &val,
+ 1, &db, &flags,
+ 2, in_keys,
+ 1, &in_val);
+ CKERR(r);
+ verify_excl_ops_fail(env,"foo.db");
+ r = txna->abort(txna); CKERR(r);
+ r = txnb->abort(txnb); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = db2->close(db2, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/diskfull.cc b/storage/tokudb/PerconaFT/src/tests/diskfull.cc
new file mode 100644
index 00000000000..0d27ad73b97
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/diskfull.cc
@@ -0,0 +1,254 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Simulate disk full by making pwrite return ENOSPC */
+/* Strategy, repeatedly run a test, and on the Ith run of the test make the Ith write fail. */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <portability/toku_atomic.h>
+
+#define DOERR(r) do { if (r!=0) { did_fail=1; fprintf(error_file, "%s:%d error %d (%s)\n", __FILE__, __LINE__, r, db_strerror(r)); }} while (0)
+
+static void
+do_db_work(void) {
+ int r;
+ int did_fail=0;
+ {
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ FILE *error_file = 0;
+ if (verbose==0) {
+ char errname[TOKU_PATH_MAX+1];
+ error_file = fopen(toku_path_join(errname, 2, TOKU_TEST_FILENAME, "stderr"), "w"); assert(error_file);
+ }
+ else error_file = stderr;
+
+ DB_ENV *env;
+ DB_TXN *tid;
+ DB *db;
+ DBT key,data;
+
+ r=db_env_create(&env, 0); assert(r==0);
+ r = env->set_redzone(env, 0); CKERR(r);
+ env->set_errfile(env, error_file ? error_file : stderr);
+ // Don't set the lg bsize for the small experiment.
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); DOERR(r);
+ if (did_fail) {
+ r=tid->abort(tid); CKERR(r);
+ } else {
+ r=tid->commit(tid, 0); DOERR(r);
+ }
+ if (did_fail) goto shutdown1;
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->put(db, tid, dbt_init(&key, "a", 2), dbt_init(&data, "b", 2), 0); DOERR(r);
+ if (did_fail) {
+ r = tid->abort(tid); CKERR2s(r, 0, ENOSPC);
+ } else {
+ r=tid->commit(tid, 0); DOERR(r);
+ }
+
+ shutdown1:
+ r=db->close(db, 0); DOERR(r);
+ r=env->close(env, 0); DOERR(r);
+ if (error_file && error_file!=stderr) fclose(error_file);
+ if (did_fail) return;
+ }
+ {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ FILE *error_file = 0;
+ if (verbose==0) {
+ char errname[TOKU_PATH_MAX+1];
+ error_file = fopen(toku_path_join(errname, 2, TOKU_TEST_FILENAME, "stderr"), "w"); assert(error_file);
+ }
+ else error_file = stderr;
+
+ DB_ENV *env;
+ DB_TXN *tid;
+ DB *db;
+ DBT key,data;
+
+ // Repeat with more put operations
+ r=db_env_create(&env, 0); assert(r==0);
+ r = env->set_redzone(env, 0); CKERR(r);
+ env->set_errfile(env, error_file ? error_file : stderr);
+ r=env->set_lg_bsize(env, 4096); assert(r==0);
+ r=env->set_cachesize(env, 0, 1, 1); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->set_pagesize(db, 4096);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); DOERR(r);
+ if (did_fail) {
+ r = tid->abort(tid); CKERR2s(r, 0, ENOSPC);
+ } else {
+ r=tid->commit(tid, 0); DOERR(r);
+ }
+ if (did_fail) goto shutdown2;
+
+ // Put an extra item in
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->put(db, tid, dbt_init(&key, "a", 2), dbt_init(&data, "b", 2), 0); DOERR(r);
+ if (did_fail) {
+ r=tid->abort(tid); CKERR(r);
+ } else {
+ r=tid->commit(tid, 0); DOERR(r);
+ }
+ if (did_fail) goto shutdown2;
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ {
+ int i;
+ for (i=0; i<100; i++) {
+ int kvsize=50;
+ int kvsize_i = kvsize / sizeof(int);
+ int keyi[kvsize_i],vali[kvsize_i];
+ int j;
+ keyi[0] = vali[0] = toku_htonl(i);
+ for (j=1; j<kvsize_i; j++) {
+ keyi[j] = random();
+ vali[j] = random();
+ }
+ r=db->put(db, tid, dbt_init(&key, keyi, sizeof keyi), dbt_init(&data, vali, sizeof vali), 0);
+ DOERR(r);
+ if (did_fail) goto break_out_of_loop;
+ }
+ }
+ break_out_of_loop:
+ //system("ls -l " TOKU_TEST_FILENAME);
+ if (did_fail) {
+ r = tid->abort(tid); CKERR2s(r, 0, ENOSPC);
+ } else {
+ r=tid->commit(tid, 0); DOERR(r);
+ }
+ shutdown2:
+ r=db->close(db, 0); DOERR(r);
+ r=env->close(env, 0); DOERR(r);
+ if (error_file && error_file!=stderr) fclose(error_file);
+ }
+}
+
+static volatile int write_count = 0;
+#define FAIL_NEVER 0x7FFFFFFF
+static int fail_at = FAIL_NEVER;
+
+static ssize_t
+pwrite_counting_and_failing (int fd, const void *buf, size_t size, toku_off_t off)
+{
+ int this_count = toku_sync_add_and_fetch(&write_count, 1);
+ if (this_count>fail_at) {
+ if (verbose>1) { printf("Failure imminent at %d:\n", fail_at); fflush(stdout); }
+ errno = ENOSPC;
+ return -1;
+ } else {
+ return pwrite(fd, buf, size, off);
+ }
+}
+
+static ssize_t
+write_counting_and_failing (int fd, const void *buf, size_t size)
+{
+ int this_count = toku_sync_add_and_fetch(&write_count, 1);
+ if (this_count>fail_at) {
+ if (verbose>1) { printf("Failure imminent at %d:\n", fail_at); fflush(stdout); }
+ errno = ENOSPC;
+ return -1;
+ } else {
+ return write(fd, buf, size);
+ }
+}
+
+static void
+do_writes_that_fail (void) {
+ if (verbose) { printf("About to fail at %d:\n", fail_at); fflush(stdout); }
+ toku_set_assert_on_write_enospc(true);
+ db_env_set_func_pwrite(pwrite_counting_and_failing);
+ db_env_set_func_full_pwrite(pwrite_counting_and_failing);
+ db_env_set_func_write (write_counting_and_failing);
+ db_env_set_func_full_write (write_counting_and_failing);
+ write_count=0;
+ do_db_work();
+ if (fail_at != FAIL_NEVER && write_count <= fail_at) {
+ abort(); // if we don't encounter the write (because there are not enough), then in fail_at mode, we need to abort so that the test will be happy.
+ }
+ printf("%d", write_count);
+}
+
+static void
+diskfull_parse_args (int argc, char * const argv[]) {
+ int c;
+ char *argv0 = argv[0];
+ while ((c = getopt(argc, (char * const *)argv, "cC:vq")) != -1) {
+ switch(c) {
+ case 'C':
+ fail_at = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'q':
+ verbose--;
+ if (verbose<0) verbose=0;
+ break;
+ default:
+do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q] [-C number]\n", argv0);
+ exit(1);
+ }
+ }
+ if (argc!=optind) {
+ goto do_usage;
+ }
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ diskfull_parse_args(argc, argv);
+ do_writes_that_fail();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/drd.suppressions b/storage/tokudb/PerconaFT/src/tests/drd.suppressions
new file mode 100644
index 00000000000..ccf7abfe0ad
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/drd.suppressions
@@ -0,0 +1,107 @@
+{
+ open_problem
+ drd:ConflictingAccess
+ fun:open64
+}
+
+{
+ read_problem
+ drd:ConflictingAccess
+ fun:read
+}
+
+{
+ write_problem
+ drd:ConflictingAccess
+ fun:write
+}
+
+{
+ pread_problem
+ drd:ConflictingAccess
+ fun:pread
+}
+
+{
+ pwrite_problem
+ drd:ConflictingAccess
+ fun:pwrite
+}
+
+{
+ fsync_problem
+ drd:ConflictingAccess
+ fun:fsync
+}
+
+{
+ lseek_problem
+ drd:ConflictingAccess
+ fun:lseek
+}
+
+{
+ close_problem
+ drd:ConflictingAccess
+ fun:close
+}
+
+{
+ centos62_open_problem
+ drd:ConflictingAccess
+ fun:open
+}
+
+{
+ centos62_pthread_clone_problem
+ drd:ConflictingAccess
+ fun:clone
+}
+{
+ EvictorSignalUnlocked
+ drd:CondRaceErr
+ fun:pthread_cond_signal@*
+ fun:_ZL16toku_cond_signalP9toku_cond
+ fun:_ZN7evictor22signal_eviction_threadEv
+}
+
+{
+ <insert_a_suppression_name_here>
+ drd:ConflictingAccess
+ ...
+ fun:_dl_runtime_resolve
+}
+{
+ <insert_a_suppression_name_here>
+ drd:ConflictingAccess
+ ...
+ fun:random
+}
+
+{
+ unsafe_touch_clock
+ drd:ConflictingAccess
+ fun:_ZL18unsafe_touch_clockP6ftnodei
+ ...
+}
+{
+ unsafe_read_single_txnid_optimization_possible
+ drd:ConflictingAccess
+ fun:_ZNK4toku8locktree46unsafe_read_single_txnid_optimization_possibleEv
+}
+{
+ unsafe_read_size_current
+ drd:ConflictingAccess
+ fun:_ZNK7evictor24unsafe_read_size_currentEv
+}
+{
+ signal_ev_thread_without_holding_mutex_for_condition
+ drd:CondRaceErr
+ fun:pthread_cond_signal@*
+ fun:_ZN7evictor22signal_eviction_threadEv
+}
+{
+ locktree_single_txn_optimization_unsafe_read_txn_is_valid
+ drd:ConflictingAccess
+ fun:_ZNK4toku8locktree25sto_txnid_is_valid_unsafeEv
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/dump-env.cc b/storage/tokudb/PerconaFT/src/tests/dump-env.cc
new file mode 100644
index 00000000000..4a434968a83
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/dump-env.cc
@@ -0,0 +1,128 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+DB_TXN *txn;
+
+
+static void
+setup (void) {
+ int r;
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r != 0) {
+ CKERR2(errno, EEXIST);
+ }
+
+ r=db_env_create(&env, 0); CKERR(r);
+ r=env->set_redzone(env, 0); CKERR(r);
+ r=env->set_default_bt_compare(env, int_dbt_cmp); CKERR(r);
+ env->set_errfile(env, stderr);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int r;
+ r= db->close(db, 0); CKERR(r);
+ r= env->close(env, 0); CKERR(r);
+}
+
+static void
+doit(void) {
+ int r;
+
+ DBC *dbc;
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = env->get_cursor_for_persistent_environment(env, txn, &dbc); CKERR(r);
+ DBT key;
+ DBT val;
+ dbt_init_realloc(&key);
+ dbt_init_realloc(&val);
+
+ while ((r = dbc->c_get(dbc, &key, &val, DB_NEXT)) == 0) {
+ if (verbose) {
+ printf("ENTRY\n\tKEY [%.*s]",
+ key.size,
+ (char*)key.data);
+ if (val.size == sizeof(uint32_t)) {
+ //assume integer
+ printf("\n\tVAL [%" PRIu32"]\n",
+ toku_dtoh32(*(uint32_t*)val.data));
+ } else if (val.size == sizeof(uint64_t)) {
+ //assume 64 bit integer
+ printf("\n\tVAL [%" PRIu64"]\n",
+ toku_dtoh64(*(uint64_t*)val.data));
+ } else {
+ printf("\n\tVAL [%.*s]\n",
+ val.size,
+ (char*)val.data);
+ }
+ }
+ }
+ CKERR2(r, DB_NOTFOUND);
+ r = dbc->c_close(dbc);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ toku_free(key.data);
+ toku_free(val.data);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+
+ setup();
+ doit();
+ test_shutdown();
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/env-put-multiple.cc b/storage/tokudb/PerconaFT/src/tests/env-put-multiple.cc
new file mode 100644
index 00000000000..5d1d0b69429
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/env-put-multiple.cc
@@ -0,0 +1,322 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test makes sure the LSN filtering is used during recovery of put_multiple
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+enum {MAX_DBS = 64, MAX_KEY = 8, MAX_VAL = 8};
+DB *dbs_multiple[MAX_DBS];
+DB *dbs_single[MAX_DBS];
+char names_single[MAX_DBS][sizeof("dbs_0xFFF")];
+char names_multiple[MAX_DBS][sizeof("dbm_0xFFF")];
+uint32_t num_dbs;
+uint32_t flags[MAX_DBS];
+uint32_t ids[MAX_DBS];
+uint32_t kbuf[MAX_DBS][MAX_KEY/4];
+uint32_t vbuf[MAX_DBS][MAX_VAL/4];
+DBT dest_keys[MAX_DBS];
+DBT dest_vals[MAX_DBS];
+
+#define CKERRIFNOT0(r) do { if (num_dbs>0) { CKERR(r); } else { CKERR2(r, EINVAL); } } while (0)
+#define CKERR2IFNOT0(r, rexpect) do { if (num_dbs>0) { CKERR2(r, rexpect); } else { CKERR2(r, EINVAL); } } while (0)
+
+static int
+put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys_arrays, DBT_ARRAY *dest_datas, const DBT *src_key, const DBT *src_data) {
+ toku_dbt_array_resize(dest_keys_arrays, 1);
+ toku_dbt_array_resize(dest_datas, 1);
+ DBT *dest_key = &dest_keys_arrays->dbts[0];
+ DBT *dest_data = &dest_datas->dbts[0];
+ dest_key->flags = 0;
+ dest_data->flags = 0;
+
+ (void) src_db;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+ assert(which < MAX_DBS);
+
+ assert(src_key->size == 4);
+ assert(src_data->size == 4);
+ kbuf[which][0] = *(uint32_t*)src_key->data;
+ kbuf[which][1] = which;
+ vbuf[which][0] = which;
+ vbuf[which][1] = *(uint32_t*)src_data->data;
+ dest_key->data = kbuf[which];
+ dest_key->size = sizeof(kbuf[which]);
+ dest_data->data = vbuf[which];
+ dest_data->size = sizeof(vbuf[which]);
+ return 0;
+}
+
+static void run_test (void) {
+ int r;
+ if (verbose)
+ printf("env-put-multiple num_dbs[%u]\n", num_dbs);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ uint32_t which;
+ {
+ //Create dbs.
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ DB *db;
+ for (which = 0; which < num_dbs; which++) {
+ ids[which] = which;
+ r = db_create(&dbs_multiple[which], env, 0);
+ CKERR(r);
+ db = dbs_multiple[which];
+ r = db->open(db, txn, names_multiple[which], NULL, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ db->app_private = &ids[which];
+ r = db_create(&dbs_single[which], env, 0);
+ CKERR(r);
+ db = dbs_single[which];
+ r = db->open(db, txn, names_single[which], NULL, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ }
+
+
+ uint32_t magic = 0xDEADBEEF;
+ // txn_begin; insert magic number
+ {
+ for (which = 0; which < num_dbs; which++) {
+ flags[which] = 0;
+ }
+ memset(flags, 0, sizeof(flags)); //reset
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ uint32_t magic2 = ~magic;
+ DBT keydbt = {.data=&magic, .size=sizeof(magic)};
+ DBT valdbt = {.data=&magic2, .size=sizeof(magic2)};
+ r = env_put_multiple_test_no_array(env, NULL, txn, &keydbt, &valdbt, num_dbs, dbs_multiple, dest_keys, dest_vals, flags);
+ CKERRIFNOT0(r);
+ for (which = 0; which < num_dbs; which++) {
+ DBT key={.data = kbuf[which], .size = sizeof(kbuf[which])};
+ DBT val={.data = vbuf[which], .size = sizeof(vbuf[which])};
+ DB *db = dbs_single[which];
+ r = db->put(db, txn, &key, &val, flags[which]);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ }
+ {
+ //Insert again with 0, expect it to work.
+ for (which = 0; which < num_dbs; which++) {
+ flags[which] = 0;
+ }
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ uint32_t magic2 = ~magic;
+ DBT keydbt = {.data=&magic, .size=sizeof(magic)};
+ DBT valdbt = {.data=&magic2, .size=sizeof(magic2)};
+ r = env_put_multiple_test_no_array(env, NULL, txn, &keydbt, &valdbt, num_dbs, dbs_multiple, dest_keys, dest_vals, flags);
+ CKERRIFNOT0(r);
+ for (which = 0; which < num_dbs; which++) {
+ DBT key={.data = kbuf[which], .size = sizeof(kbuf[which])};
+ DBT val={.data = vbuf[which], .size = sizeof(vbuf[which])};
+ DB *db = dbs_single[which];
+ r = db->put(db, txn, &key, &val, flags[which]);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ }
+ {
+ //Insert again with DB_NOOVERWRITE, expect it to fail (unless 0 dbs).
+ for (which = 0; which < num_dbs; which++) {
+ flags[which] = DB_NOOVERWRITE;
+ }
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ uint32_t magic2 = ~magic;
+ DBT keydbt = {.data=&magic, .size=sizeof(magic)};
+ DBT valdbt = {.data=&magic2, .size=sizeof(magic2)};
+ r = env_put_multiple_test_no_array(env, NULL, txn, &keydbt, &valdbt, num_dbs, dbs_multiple, dest_keys, dest_vals, flags);
+ CKERR2IFNOT0(r, DB_KEYEXIST);
+ for (which = 0; which < num_dbs; which++) {
+ DBT key={.data = kbuf[which], .size = sizeof(kbuf[which])};
+ DBT val={.data = vbuf[which], .size = sizeof(vbuf[which])};
+ DB *db = dbs_single[which];
+ r = db->put(db, txn, &key, &val, flags[which]);
+ CKERR2(r, DB_KEYEXIST);
+ }
+ r = txn->commit(txn, 0);
+ }
+
+ {
+ //Different number
+ magic = 0xFEEDADAD;
+ //Insert again with 0, using 2 transactions, expect it to fail (unless 0 dbs).
+ for (which = 0; which < num_dbs; which++) {
+ flags[which] = 0;
+ }
+ DB_TXN *txna;
+ r = env->txn_begin(env, NULL, &txna, 0);
+ CKERR(r);
+
+ uint32_t magic2 = ~magic;
+ DBT keydbt = {.data=&magic, .size=sizeof(magic)};
+ DBT valdbt = {.data=&magic2, .size=sizeof(magic2)};
+ r = env_put_multiple_test_no_array(env, NULL, txna, &keydbt, &valdbt, num_dbs, dbs_multiple, dest_keys, dest_vals, flags);
+ CKERRIFNOT0(r);
+ for (which = 0; which < num_dbs; which++) {
+ DBT key={.data = kbuf[which], .size = sizeof(kbuf[which])};
+ DBT val={.data = vbuf[which], .size = sizeof(vbuf[which])};
+ DB *db = dbs_single[which];
+ r = db->put(db, txna, &key, &val, flags[which]);
+ CKERR(r);
+ }
+
+ DB_TXN *txnb;
+ r = env->txn_begin(env, NULL, &txnb, 0);
+ CKERR(r);
+
+ //Lock should fail
+ r = env_put_multiple_test_no_array(env, NULL, txnb, &keydbt, &valdbt, num_dbs, dbs_multiple, dest_keys, dest_vals, flags);
+ CKERR2IFNOT0(r, DB_LOCK_NOTGRANTED);
+ for (which = 0; which < num_dbs; which++) {
+ DBT key={.data = kbuf[which], .size = sizeof(kbuf[which])};
+ DBT val={.data = vbuf[which], .size = sizeof(vbuf[which])};
+ DB *db = dbs_single[which];
+ r = db->put(db, txnb, &key, &val, flags[which]);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ }
+ r = txna->commit(txna, 0);
+
+ //Should succeed this time.
+ r = env_put_multiple_test_no_array(env, NULL, txnb, &keydbt, &valdbt, num_dbs, dbs_multiple, dest_keys, dest_vals, flags);
+ CKERRIFNOT0(r);
+ for (which = 0; which < num_dbs; which++) {
+ DBT key={.data = kbuf[which], .size = sizeof(kbuf[which])};
+ DBT val={.data = vbuf[which], .size = sizeof(vbuf[which])};
+ DB *db = dbs_single[which];
+ r = db->put(db, txnb, &key, &val, flags[which]);
+ CKERR(r);
+ }
+
+ r = txnb->commit(txnb, 0);
+ }
+
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ DBC *c_single;
+ DBC *c_multiple;
+
+ DBT k_single, v_single, k_multiple, v_multiple;
+ memset(&k_single, 0, sizeof(k_single));
+ memset(&v_single, 0, sizeof(v_single));
+ memset(&k_multiple, 0, sizeof(k_multiple));
+ memset(&v_multiple, 0, sizeof(v_multiple));
+ for (which = 0; which < num_dbs; which++) {
+ r = dbs_multiple[which]->cursor(dbs_multiple[which], txn, &c_multiple, 0);
+ CKERR(r);
+ r = dbs_single[which]->cursor(dbs_single[which], txn, &c_single, 0);
+ CKERR(r);
+
+ int r1 = 0;
+ int r2;
+ while (r1 == 0) {
+ r1 = c_single->c_get(c_single, &k_single, &v_single, DB_NEXT);
+ r2 = c_multiple->c_get(c_multiple, &k_multiple, &v_multiple, DB_NEXT);
+ assert(r1==r2);
+ CKERR2s(r1, 0, DB_NOTFOUND);
+ if (r1 == 0) {
+ assert(k_single.size == k_multiple.size);
+ assert(v_single.size == v_multiple.size);
+ assert(memcmp(k_single.data, k_multiple.data, k_single.size) == 0);
+ assert(memcmp(v_single.data, v_multiple.data, v_single.size) == 0);
+ }
+ }
+ r = c_single->c_close(c_single);
+ CKERR(r);
+ r = c_multiple->c_close(c_multiple);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ }
+ {
+ for (which = 0; which < num_dbs; which++) {
+ r = dbs_single[which]->close(dbs_single[which], 0);
+ CKERR(r);
+ r = dbs_multiple[which]->close(dbs_multiple[which], 0);
+ CKERR(r);
+ }
+ }
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ uint32_t which;
+ for (which = 0; which < MAX_DBS; which++) {
+ sprintf(names_multiple[which], "dbm_0x%02X", which);
+ sprintf(names_single[which], "dbs_0x%02X", which);
+ dbt_init(&dest_keys[which], NULL, 0);
+ dbt_init(&dest_vals[which], NULL, 0);
+ }
+ for (num_dbs = 0; num_dbs < 4; num_dbs++) {
+ run_test();
+ }
+ for (num_dbs = 4; num_dbs <= MAX_DBS; num_dbs *= 2) {
+ run_test();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/env_loader_memory.cc b/storage/tokudb/PerconaFT/src/tests/env_loader_memory.cc
new file mode 100644
index 00000000000..445ed804851
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/env_loader_memory.cc
@@ -0,0 +1,62 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+
+static uint64_t my_loader_memory_size;
+
+static uint64_t get_loader_memory_size(void) {
+ return my_loader_memory_size;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ assert_zero(r);
+ env->set_loader_memory_size(env, get_loader_memory_size);
+ for (uint64_t n = 0 ; n < 10000000000; n += 1000000000) {
+ my_loader_memory_size = n;
+ assert(env->get_loader_memory_size(env) == n);
+ }
+ r = env->close(env, 0);
+ assert_zero(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/env_nproc.cc b/storage/tokudb/PerconaFT/src/tests/env_nproc.cc
new file mode 100644
index 00000000000..9b5415e7a2a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/env_nproc.cc
@@ -0,0 +1,90 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/resource.h>
+
+static void env_open_close(void) {
+ int r;
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK+DB_INIT_MPOOL+DB_INIT_TXN+DB_INIT_LOG + DB_CREATE + DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r != 0) {
+ fprintf(stderr, "%s:%u r=%d\n", __FILE__, __LINE__, r);
+ }
+ r = env->close(env, 0);
+ assert(r == 0);
+}
+
+int test_main (int argc, char * const argv[]) {
+ int r;
+ int limit = 1;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0) verbose--;
+ continue;
+ }
+ limit = atoi(argv[i]);
+ continue;
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ struct rlimit nproc_rlimit;
+ r = getrlimit(RLIMIT_NPROC, &nproc_rlimit);
+ assert(r == 0);
+
+ nproc_rlimit.rlim_cur = limit;
+ r = setrlimit(RLIMIT_NPROC, &nproc_rlimit);
+ assert(r == 0);
+
+ env_open_close();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/env_startup.cc b/storage/tokudb/PerconaFT/src/tests/env_startup.cc
new file mode 100644
index 00000000000..e6d704e882b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/env_startup.cc
@@ -0,0 +1,197 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Purpose of this test is to verify correct behavior of
+ * environment startup:
+ *
+ * All three of the following should exist or all three should not exist:
+ * - persistent environment
+ * - fileops directory
+ * - recovery log (if DB_INIT_LOG)
+ *
+ * If all three are missing, env->open() should create a new environment.
+ * If any one is present and any other is missing, env->open() should return ENOENT.
+ *
+ * TODO: experiment with DB_INIT_LOG off.
+ */
+
+
+#include "test.h"
+#include <db.h>
+
+static DB_ENV *env;
+
+#define FLAGS_NOLOG DB_INIT_LOCK|DB_INIT_MPOOL|DB_CREATE|DB_PRIVATE
+#define FLAGS_LOG FLAGS_NOLOG|DB_INIT_TXN|DB_INIT_LOG
+
+static int mode = S_IRWXU+S_IRWXG+S_IRWXO;
+
+static void test_shutdown(void);
+
+static void
+setup (uint32_t flags) {
+ int r;
+ if (env)
+ test_shutdown();
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r=db_env_create(&env, 0);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, flags, mode);
+ CKERR(r);
+}
+
+
+
+static void
+test_shutdown(void) {
+ int r;
+ r=env->close(env, 0); CKERR(r);
+ env = NULL;
+}
+
+
+static void
+reopen_env(uint32_t flags, int expected_r) {
+ int r;
+ if (env)
+ test_shutdown();
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, flags, mode);
+ CKERR2(r, expected_r);
+}
+
+static void
+delete_persistent(void) {
+ char cmd[1024];
+ sprintf(cmd, "rm -rf %s%s%s", TOKU_TEST_FILENAME, "/", "tokudb.environment");
+ int r = system(cmd);
+ CKERR(r);
+}
+
+
+static void
+delete_directory(void) {
+ char cmd[1024];
+ sprintf(cmd, "rm -rf %s%s%s", TOKU_TEST_FILENAME, "/", "tokudb.directory");
+ int r = system(cmd);
+ CKERR(r);
+}
+
+
+static void
+delete_log(void) {
+ char cmd[1024];
+ sprintf(cmd, "rm -rf %s%s%s", TOKU_TEST_FILENAME, "/", "*.tokulog*");
+ int r = system(cmd);
+ CKERR(r);
+}
+
+
+static void
+create_env(uint32_t flags) {
+ setup(flags); // create new environment
+ test_shutdown();
+ reopen_env(flags, 0); // reopen existing environment, should have log now
+ test_shutdown();
+}
+
+
+static void
+test_env_startup(int logging) {
+ uint32_t flags;
+
+ if (logging)
+ flags = FLAGS_LOG;
+ else
+ flags = FLAGS_NOLOG;
+
+ create_env(flags);
+
+ // delete persistent info and try to reopen
+ delete_persistent();
+ reopen_env(flags, ENOENT);
+
+ // recreate, then try to open with missing fileops directory
+ create_env(flags);
+ delete_directory();
+ reopen_env(flags, ENOENT);
+
+
+ if (logging) {
+ // recreate, then try to open with missing recovery log
+ create_env(flags);
+ delete_log();
+ reopen_env(flags, ENOENT);
+
+
+ // now try two missing items, if log can be present
+
+ // log is only item present
+ create_env(flags);
+ delete_persistent();
+ delete_directory();
+ reopen_env(flags, ENOENT);
+
+ // persistent env is only item present
+ create_env(flags);
+ delete_log();
+ delete_directory();
+ reopen_env(flags, ENOENT);
+
+ // directory is only item present
+ create_env(flags);
+ delete_persistent();
+ delete_log();
+ reopen_env(flags, ENOENT);
+ }
+
+ test_shutdown();
+}
+
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ test_env_startup(0); // transactionless env
+ test_env_startup(1); // with transactions and logging
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/filesize.cc b/storage/tokudb/PerconaFT/src/tests/filesize.cc
new file mode 100644
index 00000000000..6ab22245e68
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/filesize.cc
@@ -0,0 +1,269 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Idea:
+ * create a dictionary
+ * repeat:
+ * lots of inserts
+ * checkpoint
+ * note file size
+ * lots of deletes
+ * optimize (flatten tree)
+ * checkpoint
+ * note file size
+ *
+ */
+
+#include "test.h"
+
+#define PATHSIZE 1024
+
+DB_ENV *env;
+DB *db;
+char dbname[] = "foo.db";
+char path[PATHSIZE];
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_PRIVATE;
+
+int ninsert, nread, nread_notfound, nread_failed, ndelete, ndelete_notfound, ndelete_failed;
+
+static TOKU_DB_FRAGMENTATION_S report;
+
+static void
+check_fragmentation(void) {
+ int r = db->get_fragmentation(db, &report);
+ CKERR(r);
+}
+
+static void
+print_fragmentation(void) {
+ printf("Fragmentation:\n");
+ printf("\tTotal file size in bytes (file_size_bytes): %" PRIu64 "\n", report.file_size_bytes);
+ printf("\tCompressed User Data in bytes (data_bytes): %" PRIu64 "\n", report.data_bytes);
+ printf("\tNumber of blocks of compressed User Data (data_blocks): %" PRIu64 "\n", report.data_blocks);
+ printf("\tAdditional bytes used for checkpoint system (checkpoint_bytes_additional): %" PRIu64 "\n", report.checkpoint_bytes_additional);
+ printf("\tAdditional blocks used for checkpoint system (checkpoint_blocks_additional): %" PRIu64 "\n", report.checkpoint_blocks_additional);
+ printf("\tUnused space in file (unused_bytes): %" PRIu64 "\n", report.unused_bytes);
+ printf("\tNumber of contiguous regions of unused space (unused_blocks): %" PRIu64 "\n", report.unused_blocks);
+ printf("\tSize of largest contiguous unused space (largest_unused_block): %" PRIu64 "\n", report.largest_unused_block);
+}
+
+static void
+close_em (void)
+{
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+static void
+setup(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+}
+
+
+static void
+fill_rand(int n, uint64_t * d) {
+ for (int i = 0; i < n; i++){
+ *(d+i) = random64();
+ }
+}
+
+#define INSERT_BIG 1500
+#define INSERT_SMALL 0
+static void
+insert_n (uint32_t ah, int datasize) {
+ uint64_t vdata[datasize];
+ fill_rand(datasize, vdata);
+ uint32_t an = htonl(ah);
+ // if (verbose) printf("insert an = %0X (ah = %0X)\n", an, ah);
+ DBT key;
+ dbt_init(&key, &an, 4);
+ DBT val;
+ dbt_init(&val, vdata, sizeof vdata);
+ int r = db->put(db, NULL, &key, &val, 0);
+ CKERR(r);
+ ninsert++;
+}
+
+static void
+delete_n (uint32_t ah)
+{
+ uint32_t an = htonl(ah);
+ // if (verbose) printf("delete an = %0X (ah = %0X)\n", an, ah);
+ DBT key;
+ dbt_init(&key, &an, 4);
+ int r = db->del(db, NULL, &key, DB_DELETE_ANY);
+ if (r == 0)
+ ndelete++;
+ else if (r == DB_NOTFOUND)
+ ndelete_notfound++;
+ else
+ ndelete_failed++;
+ CKERR(r);
+}
+
+static void
+optimize(void) {
+ if (verbose) printf("Filesize: begin optimize dictionary\n");
+ uint64_t loops_run;
+ int r = db->hot_optimize(db, NULL, NULL, NULL, NULL, &loops_run);
+ CKERR(r);
+ if (verbose) printf("Filesize: end optimize dictionary\n");
+}
+
+
+static void
+get_file_pathname(void) {
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, dbname, sizeof(dbname));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ int r = env->get_iname(env, &dname, &iname);
+ CKERR(r);
+ sprintf(path, "%s/%s", TOKU_TEST_FILENAME, (char*)iname.data);
+ toku_free(iname.data);
+ if (verbose) printf("path = %s\n", path);
+}
+
+
+static int
+getsizeM(void) {
+ toku_struct_stat buf;
+ int r = toku_stat(path, &buf);
+ CKERR(r);
+ int sizeM = (int)buf.st_size >> 20;
+ check_fragmentation();
+ if (verbose>1)
+ print_fragmentation();
+ return sizeM;
+}
+
+static void
+test_filesize (bool sequential)
+{
+ int N=1<<14;
+ int r, i, sizeM;
+
+ get_file_pathname();
+
+ for (int iter = 0; iter < 3; iter++) {
+ int offset = N * iter;
+
+ if (sequential) {
+ for (i=0; i<N; i++) {
+ insert_n(i + offset, INSERT_BIG);
+ }
+ } else {
+ for (i=N-1; i>=0; --i) {
+ insert_n(i + offset, INSERT_BIG);
+ }
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+ int sizefirst = sizeM = getsizeM();
+ if (verbose) printf("Filesize after iteration %d insertion and checkpoint = %dM\n", iter, sizeM);
+
+ int preserve = 2;
+ for (i = preserve; i<(N); i++) { // leave a little at the beginning
+ delete_n(i + offset);
+ }
+ optimize();
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+ sizeM = getsizeM();
+ if (verbose) printf("Filesize after iteration %d deletion and checkpoint 1 = %dM\n", iter, sizeM);
+
+ if (sequential) {
+ for (i=0; i<N; i++) {
+ insert_n(i + offset, INSERT_SMALL);
+ }
+ } else {
+ for (i=N-1; i>=0; --i) {
+ insert_n(i + offset, INSERT_SMALL);
+ }
+ }
+ for (i = preserve; i<(N); i++) { // leave a little at the beginning
+ delete_n(i + offset);
+ }
+ optimize();
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+ sizeM = getsizeM();
+ if (verbose) printf("Filesize after iteration %d deletion and checkpoint 2 = %dM\n", iter, sizeM);
+ assert(sizeM <= sizefirst);
+
+ if (verbose) printf("ninsert = %d\n", ninsert);
+ if (verbose) printf("nread = %d, nread_notfound = %d, nread_failed = %d\n", nread, nread_notfound, nread_failed);
+ if (verbose) printf("ndelete = %d, ndelete_notfound = %d, ndelete_failed = %d\n", ndelete, ndelete_notfound, ndelete_failed);
+ }
+}
+
+int test_main (int argc __attribute__((__unused__)), char * const argv[] __attribute__((__unused__))) {
+ parse_args(argc, argv);
+ setup();
+ if (verbose) print_engine_status(env);
+ test_filesize(true);
+ if (verbose) {
+ print_engine_status(env);
+ }
+ check_fragmentation();
+ if (verbose) print_fragmentation();
+ close_em();
+ setup();
+ if (verbose) print_engine_status(env);
+ test_filesize(false);
+ if (verbose) {
+ print_engine_status(env);
+ }
+ check_fragmentation();
+ if (verbose) print_fragmentation();
+ close_em();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/get_key_after_bytes_unit.cc b/storage/tokudb/PerconaFT/src/tests/get_key_after_bytes_unit.cc
new file mode 100644
index 00000000000..70e1bddc4a1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/get_key_after_bytes_unit.cc
@@ -0,0 +1,247 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <algorithm>
+
+// Unit test for db->get_key_after_bytes.
+
+static const int num_keys = 1<<10;
+
+static void setup(DB_ENV **envp, DB **dbp, uint32_t nodesize, uint32_t basementnodesize) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ r = db_env_create(envp, 0);
+ CKERR(r);
+ DB_ENV *env = *envp;
+ r = env->set_default_bt_compare(env, int_dbt_cmp);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ r = db_create(dbp, env, 0);
+ CKERR(r);
+ DB *db = *dbp;
+ {
+ r = db->set_pagesize(db, nodesize);
+ CKERR(r);
+ r = db->set_readpagesize(db, basementnodesize);
+ CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+}
+
+static void fill(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+ int k, v;
+ DBT key, val;
+ dbt_init(&key, &k, sizeof k);
+ dbt_init(&val, &v, sizeof v);
+ for (int i = 0; i < num_keys; ++i) {
+ k = i;
+ v = i;
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+}
+
+struct check_extra {
+ int start_key;
+ uint64_t skip_len;
+ bool filled;
+ bool exact;
+};
+
+static void check_callback(const DBT *end_key, uint64_t actually_skipped, void *extra) {
+ struct check_extra *CAST_FROM_VOIDP(e, extra);
+
+ int real_start_key = std::min(std::max(e->start_key, 0), num_keys);
+ int expected_key = std::min(real_start_key + (e->skip_len / (2 * sizeof(int))), (uint64_t) num_keys);
+
+ if (e->exact) {
+ if (!e->filled || expected_key >= num_keys) {
+ expected_key = -1;
+ }
+ assert(actually_skipped <= e->skip_len);
+ if (expected_key == -1) {
+ assert(end_key == nullptr);
+ } else {
+ assert(e->skip_len - actually_skipped < 2 * (int) sizeof(int));
+ assert(end_key != nullptr);
+ assert(end_key->size == sizeof expected_key);
+ assert((*(int *) end_key->data) == expected_key);
+ }
+ } else {
+ // no sense in doing an inexact check if the table's empty
+ assert(e->filled);
+ int found;
+ if (end_key == nullptr) {
+ found = num_keys;
+ } else {
+ assert(end_key->size == sizeof found);
+ found = *(int *) end_key->data;
+ }
+ // These are just guesses. I don't have a good reason but they
+ // seem like alright bounds.
+ double skipped_portion = (double) e->skip_len / (num_keys * 2 * sizeof(int));
+ int key_slack = num_keys * std::max(std::min(skipped_portion, 0.25), 0.01);
+ int size_slack = key_slack * 2 * sizeof(int);
+ assert(found <= expected_key + key_slack);
+ assert(found >= expected_key - key_slack);
+ assert(actually_skipped <= e->skip_len + size_slack);
+ if (end_key != nullptr) {
+ // if we hit the end of the table, this definitely won't hold up
+ assert((int) actually_skipped >= (int) e->skip_len - size_slack);
+ }
+ }
+}
+
+static void check(DB_ENV *env, DB *db, int start_key, uint64_t skip_len, bool filled, bool exact) {
+ int r;
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+
+ DBT start_dbt, end_key;
+ dbt_init(&start_dbt, &start_key, sizeof start_key);
+ dbt_init(&end_key, nullptr, 0);
+
+ struct check_extra extra = {start_key, skip_len, filled, exact};
+ r = db->get_key_after_bytes(db, txn, (start_key == -2 ? nullptr : &start_dbt), skip_len, check_callback, &extra, 0);
+ CKERR(r);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+}
+
+static void teardown(DB_ENV *env, DB *db) {
+ int r;
+ r = db->close(db, 0);
+ CKERR(r);
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+ default_parse_args(argc, argv);
+
+ DB_ENV *env;
+ DB *db;
+
+ setup(&env, &db, 4<<20, 64<<10);
+
+ // if the table is empty, always say DB_NOTFOUND
+ for (int start_key = -2; start_key <= 1; ++start_key) {
+ for (int skip_len = 0; skip_len < 2; ++skip_len) {
+ check(env, db, start_key, skip_len, false, true);
+ }
+ }
+
+ fill(env, db);
+
+ // if start_key is bigger than any key, assert that we get DB_NOTFOUND
+ for (int extra_key = 0; extra_key < 10; extra_key += 5) {
+ for (int skip_len = 0; skip_len < 24; ++skip_len) {
+ check(env, db, num_keys + extra_key, skip_len, true, true);
+ }
+ }
+
+ // if start_key is nullptr or the first key or before the first key, we start at the beginning
+ for (int start_key = -2; start_key <= 0; ++start_key) {
+ for (int skip_len = 0; skip_len < 48; ++skip_len) {
+ check(env, db, start_key, skip_len, true, true);
+ }
+ }
+
+ // check a bunch of places in the middle too (use prime increments to get a good distribution of stuff)
+ for (int start_key = 0; start_key <= num_keys; start_key += 31) {
+ for (int skip_len = 0; skip_len < (num_keys + 1 - start_key) * (2 * (int) sizeof(int)); skip_len += 67) {
+ check(env, db, start_key, skip_len, true, true);
+ }
+ }
+
+ // TODO: test mvcc stuff (check that we only look at the latest val, which is the current behavior)
+
+ teardown(env, db);
+
+ // Try many bn and nodesizes
+ for (int basementnodesize = 1<<10; basementnodesize <= 64<<10; basementnodesize <<= 1) {
+ for (int nodesize = basementnodesize; nodesize <= 128<<10; nodesize <<= 2) {
+ setup(&env, &db, nodesize, basementnodesize);
+ fill(env, db);
+ // forces a rebalance of the root, to get multiple bns
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+ // near the beginning
+ for (int start_key = -2; start_key <= 1; ++start_key) {
+ for (int skip_len = 0; skip_len <= (num_keys + 1 - start_key) * (2 * (int) sizeof(int)); skip_len += 41) {
+ check(env, db, start_key, skip_len, true, false);
+ }
+ }
+ // near the end
+ for (int start_key = num_keys - 1; start_key <= num_keys + 1; ++start_key) {
+ for (int skip_len = 0; skip_len <= (num_keys + 1 - start_key) * (2 * (int) sizeof(int)); skip_len += 41) {
+ check(env, db, start_key, skip_len, true, false);
+ }
+ }
+ for (int start_key = 0; start_key <= num_keys; start_key += 17) {
+ for (int skip_len = 0; skip_len <= (num_keys + 1 - start_key) * (2 * (int) sizeof(int)); skip_len += 31) {
+ check(env, db, start_key, skip_len, true, false);
+ }
+ }
+ teardown(env, db);
+ }
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/get_last_key.cc b/storage/tokudb/PerconaFT/src/tests/get_last_key.cc
new file mode 100644
index 00000000000..3749d9ea8ff
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/get_last_key.cc
@@ -0,0 +1,247 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/**
+ * Test that various queries behave correctly
+ *
+ * Zardosht says:
+ *
+ * write a test that inserts a bunch of elements into the tree,
+ * and then verify that the following types of queries work:
+ * - db->get
+ * - next
+ * - prev
+ * - set_range
+ * - set_range_reverse
+ * - first
+ * - last
+ * - current
+ *
+ * do it on a table with:
+ * - just a leaf node
+ * - has internal nodes (make node size 4K and bn size 1K)
+ * - big cachetable such that everything fits
+ * - small cachetable such that not a lot fits
+ *
+ * make sure APIs are the callback APIs (getf_XXX)
+ * make sure your callbacks all return TOKUDB_CURSOR_CONTINUE,
+ * so we ensure that returning TOKUDB_CURSOR_CONTINUE does not
+ * mess anything up.
+ */
+
+#include "test.h"
+
+/**
+ * Calculate or verify that a value for a given key is correct
+ * Returns 0 if the value is correct, nonzero otherwise.
+ */
+static void get_value_by_key(DBT * key, DBT * value)
+{
+ // keys/values are always stored in the DBT in net order
+ int * CAST_FROM_VOIDP(k, key->data);
+ int v = toku_ntohl(*k) * 2 + 1;
+ memcpy(value->data, &v, sizeof(int));
+}
+
+static void prepare_for_env(void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); { int chk_r = r; CKERR(chk_r); }
+}
+
+static void init_env(DB_ENV ** env, size_t ct_size)
+{
+ int r;
+ const int envflags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD |
+ DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE;
+
+ printf("initializing environment\n");
+
+ r = db_env_create(env, 0); { int chk_r = r; CKERR(chk_r); }
+ assert(ct_size < 1024 * 1024 * 1024L);
+ r = (*env)->set_cachesize(*env, 0, ct_size, 1); { int chk_r = r; CKERR(chk_r); }
+ r = (*env)->open(*env, TOKU_TEST_FILENAME, envflags, 0755); { int chk_r = r; CKERR(chk_r); }
+}
+
+static void init_db(DB_ENV * env, DB ** db)
+{
+ int r;
+ const int node_size = 4096;
+ const int bn_size = 1024;
+
+ printf("initializing db\n");
+
+ DB_TXN * txn;
+ r = db_create(db, env, 0); { int chk_r = r; CKERR(chk_r); }
+ r = (*db)->set_readpagesize(*db, bn_size); { int chk_r = r; CKERR(chk_r); }
+ r = (*db)->set_pagesize(*db, node_size); { int chk_r = r; CKERR(chk_r); }
+ r = env->txn_begin(env, nullptr, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = (*db)->open(*db, txn, "db", nullptr, DB_BTREE, DB_CREATE, 0644); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+}
+
+static void cleanup_env_and_db(DB_ENV * env, DB * db)
+{
+ int r;
+
+ printf("cleaning up environment and db\n");
+ r = db->close(db, 0); { int chk_r = r; CKERR(chk_r); }
+ r = env->close(env, 0); { int chk_r = r; CKERR(chk_r); }
+}
+
+static int get_last_key_cb(const DBT *key, const DBT *value, void *extra) {
+ if (key->data) {
+ invariant_null(value);
+ int expected_key = *(int*)extra;
+ int found_key = *(int*)key->data;
+ invariant(expected_key == (int)ntohl(found_key));
+ }
+ return 0;
+}
+
+
+static void check_last_key_matches(DB *db, int expect_r, int key) {
+ int r = db->get_last_key(db, get_last_key_cb, &key);
+ CKERR2(r, expect_r);
+}
+
+static void do_test(size_t ct_size, int num_keys)
+{
+ int i, r;
+ DB * db;
+ DB_ENV * env;
+ DB_TXN *txn = nullptr;
+ DB_TXN *txn2 = nullptr;
+ uint64_t loops_run = 0;
+
+
+ printf("doing tests for ct_size %lu, num_keys %d\n",
+ ct_size, num_keys);
+
+ // initialize everything and insert data
+ prepare_for_env();
+ init_env(&env, ct_size);
+ assert(env != nullptr);
+ init_db(env, &db);
+ assert(db != nullptr);
+
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+ DBT key, value;
+ for (i = 0; i < num_keys; i++) {
+ int v, k = toku_htonl(i);
+ dbt_init(&key, &k, sizeof(int));
+ dbt_init(&value, &v, sizeof(int));
+ get_value_by_key(&key, &value);
+ r = db->put(db, txn, &key, &value, 0);
+ CKERR(r);
+ }
+ CKERR(r);
+
+ int expect_r = num_keys == 0 ? DB_NOTFOUND : 0;
+ check_last_key_matches(db, expect_r, num_keys - 1);
+
+ r = txn->commit(txn, 0);
+ check_last_key_matches(db, expect_r, num_keys - 1);
+
+ if (num_keys == 0) {
+ goto cleanup;
+ }
+ r = env->txn_begin(env, nullptr, &txn2, 0);
+ CKERR(r);
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ r = db->del(db, txn, &key, 0);
+ check_last_key_matches(db, 0, num_keys - 1);
+
+ r = txn->commit(txn, 0);
+ check_last_key_matches(db, 0, num_keys - 1);
+
+ r = txn2->commit(txn2, 0);
+ check_last_key_matches(db, 0, num_keys - 1);
+
+ //Run Garbage collection (NOTE does not work when everything fits in root??? WHY)
+ r = db->hot_optimize(db, nullptr, nullptr, nullptr, nullptr, &loops_run);
+ CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ //Run Garbage collection (NOTE does not work when everything fits in root??? WHY)
+ r = db->hot_optimize(db, nullptr, nullptr, nullptr, nullptr, &loops_run);
+ CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ //Fully close and reopen
+ //This clears cachetable
+ //note that closing a db and reopening may not flush the cachetable so we close env as well
+ cleanup_env_and_db(env, db);
+ init_env(&env, ct_size);
+ assert(env != nullptr);
+ init_db(env, &db);
+ assert(db != nullptr);
+
+ //NOTE: tried overkill (double optimize, double checkpoint.. gc still doesn't happen for everything in root in single basement
+
+ if (num_keys >= 2) {
+ // At least one key remains.
+ check_last_key_matches(db, 0, num_keys - 2);
+ } else {
+ //no key remains. Should find nothing.
+ check_last_key_matches(db, DB_NOTFOUND, -1);
+ }
+cleanup:
+ cleanup_env_and_db(env, db);
+}
+
+int test_main(int argc, char * const argv[])
+{
+ default_parse_args(argc, argv);
+
+ for (int i = 0; i <= 2; i++) {
+ do_test(1024*1024, i);
+ }
+ for (int i = 4; i <= 1024; i*=2) {
+ do_test(1024*1024, i);
+ }
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/helgrind.suppressions b/storage/tokudb/PerconaFT/src/tests/helgrind.suppressions
new file mode 100644
index 00000000000..4729c1d14ad
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/helgrind.suppressions
@@ -0,0 +1,158 @@
+{
+ helgrind_3.5.0_false_positive_against_pthread_create
+ Helgrind:Race
+ fun:mythread_wrapper
+}
+{
+ ignore_race_on_toku_checkpointing_user_data_status
+ Helgrind:Race
+ fun:toku_set_checkpointing_user_data_status.*
+}
+{
+ ignore_race_on_toku_checkpointing_user_data_status
+ Helgrind:Race
+ fun:toku_get_checkpointing_user_data_status
+}
+{
+ ignore_race_inside_pthread_mutex_lock
+ Helgrind:Race
+ fun:pthread_mutex_lock
+}
+{
+ ignore_race_inside_pthread_mutex_unlock
+ Helgrind:Race
+ ...
+ fun:pthread_mutex_unlock
+}
+{
+ ignore_race_inside_pthread_join
+ Helgrind:Race
+ fun:pthread_join
+}
+{
+ unsafe_touch_clock
+ Helgrind:Race
+ fun:_ZL18unsafe_touch_clockP6ftnodei
+}
+{
+ unsafe_read_single_txnid_optimization_possible
+ Helgrind:Race
+ fun:_ZNK4toku8locktree46unsafe_read_single_txnid_optimization_possibleEv
+}
+{
+ unsafe_read_size_current
+ Helgrind:Race
+ fun:_ZNK7evictor24unsafe_read_size_currentEv
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_ZL14ctpair_destroyP6ctpair
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_Z23toku_blocktable_destroyPP11block_table
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_Z11bjm_destroyP29background_job_manager_struct
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_Z24toku_txn_manager_destroyP11txn_manager
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_Z24toku_thread_pool_destroyPP16toku_thread_pool
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_Z22toku_minicron_shutdownP8minicron
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_ZN4toku12lock_request7destroyEv
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_Z17toku_logger_closePP10tokulogger
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_ZL20cachetable_free_pairP6ctpair
+}
+{
+ kde_bug_307082_cond_destroy_without_signal
+ Helgrind:Misc
+ ...
+ fun:pthread_cond_destroy@*
+ ...
+ fun:_ZN7evictor7destroyEv
+}
+{
+ <helgrind_doesnt_understand_the_way_the_world_works_and_ignores_our_disable_checking_instructions>
+ Helgrind:Race
+ fun:_ZN4toku8locktree15sto_try_acquireEPvmPK10__toku_dbtS4_
+ fun:_ZN4toku8locktree12acquire_lockEbmPK10__toku_dbtS3_PNS_9txnid_setE
+ fun:_ZN4toku8locktree16try_acquire_lockEbmPK10__toku_dbtS3_PNS_9txnid_setEb
+ fun:_ZN4toku8locktree18acquire_write_lockEmPK10__toku_dbtS3_PNS_9txnid_setEb
+ fun:_ZN4toku12lock_request5startEv
+ ...
+}
+{
+ <helgrind_bug_323432_see_http://permalink.gmane.org/gmane.comp.debugging.valgrind/13325>
+ Helgrind:Race
+ obj:/usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so
+ fun:pthread_mutex_destroy
+ fun:toku_mutex_destroy
+ fun:_ZN4toku8treenode4freeEPS0_
+ fun:_ZN4toku8treenode22remove_root_of_subtreeEv
+ ...
+}
+{
+ helgrind_bug_https://bugs.kde.org/show_bug.cgi?id=327548
+ Helgrind:Race
+ fun:my_memcmp
+ fun:pthread_mutex_destroy
+}
+{
+ helgrind_bug_2_helgrind_bug_https://bugs.kde.org/show_bug.cgi?id=327548
+ Helgrind:Race
+ ...
+ fun:pthread_mutex_destroy
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/helgrind1.cc b/storage/tokudb/PerconaFT/src/tests/helgrind1.cc
new file mode 100644
index 00000000000..bb2b714e3b7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/helgrind1.cc
@@ -0,0 +1,65 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+// The helgrind1.tdbrun test should fail. This is merely a check to verify that helgrind actually notices a race.
+
+#include <pthread.h>
+int x;
+
+static void *starta(void* ignore __attribute__((__unused__))) {
+ if (verbose) printf("%s %d\n", __FUNCTION__, x);
+ x++;
+ return 0;
+}
+static void *startb(void* ignore __attribute__((__unused__))) {
+ if (verbose) printf("%s %d\n", __FUNCTION__, x);
+ x++;
+ return 0;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ pthread_t a,b;
+ { int x_l = pthread_create(&a, NULL, starta, NULL); assert(x_l==0); }
+ { int x_l = pthread_create(&b, NULL, startb, NULL); assert(x_l==0); }
+ { int x_l = pthread_join(a, NULL); assert(x_l==0); }
+ { int x_l = pthread_join(b, NULL); assert(x_l==0); }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/helgrind2.cc b/storage/tokudb/PerconaFT/src/tests/helgrind2.cc
new file mode 100644
index 00000000000..3060bcaff5b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/helgrind2.cc
@@ -0,0 +1,135 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+// The helgrind2 test performs a DB->get() in two different concurrent threads.
+#include <arpa/inet.h>
+
+#include <db.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <memory.h>
+
+DB_ENV *env;
+DB *db;
+
+static void initialize (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, 0777);
+
+ // setup environment
+ {
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->set_redzone(env, 0); CKERR(r);
+ env->set_errfile(env, stdout);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_PRIVATE + DB_CREATE, 0777);
+ assert(r == 0);
+ }
+
+ // setup DB
+ {
+ DB_TXN *txn = 0;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, 0777); assert(r == 0);
+ }
+
+ // Put some stuff in
+ {
+ char v[10];
+ DB_TXN *txn = 0;
+ int i;
+ const int n = 10;
+ memset(v, 0, sizeof(v));
+ for (i=0; i<n; i++) {
+ int k = htonl(i);
+ DBT key, val;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, v, sizeof(v)), 0);
+ assert(r == 0);
+ }
+ }
+}
+
+static void finish (void) {
+ int r;
+ r = db->close(db, 0); assert(r==0);
+ r = env->close(env, 0); assert(r==0);
+}
+
+static void *starta(void* ignore __attribute__((__unused__))) {
+ DB_TXN *txn = 0;
+ DBT key, val;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+ val.flags |= DB_DBT_MALLOC;
+ int k = htonl(0);
+ int r = db->get(db, txn, dbt_init(&key, &k, sizeof k), &val, 0);
+ assert(r==0);
+ //printf("val.data=%p\n", val.data);
+ int i; for (i=0; i<10; i++) assert(((char*)val.data)[i]==0);
+ toku_free(val.data);
+ return 0;
+}
+static void *startb(void* ignore __attribute__((__unused__))) {
+ DB_TXN *txn = 0;
+ DBT key, val;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+ int k = htonl(0);
+ val.flags |= DB_DBT_MALLOC;
+ int r = db->get(db, txn, dbt_init(&key, &k, sizeof k), &val, 0);
+ assert(r==0);
+ //printf("val.data=%p\n", val.data);
+ int i; for (i=0; i<10; i++) assert(((char*)val.data)[i]==0);
+ toku_free(val.data);
+ return 0;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ pthread_t a,b;
+ initialize();
+ { int x = pthread_create(&a, NULL, starta, NULL); assert(x==0); }
+ { int x = pthread_create(&b, NULL, startb, NULL); assert(x==0); }
+ { int x = pthread_join(a, NULL); assert(x==0); }
+ { int x = pthread_join(b, NULL); assert(x==0); }
+ finish();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/helgrind3.cc b/storage/tokudb/PerconaFT/src/tests/helgrind3.cc
new file mode 100644
index 00000000000..6ccf0d76b6b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/helgrind3.cc
@@ -0,0 +1,135 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+// The helgrind2 test performs a DB->get() in two different concurrent threads.
+#include <arpa/inet.h>
+
+#include <db.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <memory.h>
+
+DB_ENV *env;
+DB *db;
+
+static void initialize (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, 0777);
+
+ // setup environment
+ {
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->set_redzone(env, 0); CKERR(r);
+ env->set_errfile(env, stdout);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_PRIVATE + DB_CREATE, 0777);
+ assert(r == 0);
+ }
+
+ // setup DB
+ {
+ DB_TXN *txn = 0;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, 0777); assert(r == 0);
+ }
+
+ // Put some stuff in
+ {
+ char v[10];
+ DB_TXN *txn = 0;
+ int i;
+ const int n = 10;
+ memset(v, 0, sizeof(v));
+ for (i=0; i<n; i++) {
+ int k = htonl(i);
+ DBT key, val;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, v, sizeof(v)), 0);
+ assert(r == 0);
+ }
+ }
+}
+
+static void finish (void) {
+ int r;
+ r = db->close(db, 0); assert(r==0);
+ r = env->close(env, 0); assert(r==0);
+}
+
+static void *starta(void* ignore __attribute__((__unused__))) {
+ DB_TXN *txn = 0;
+ DBT key, val;
+ char data[10];
+ val.data = data;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+ val.flags |= DB_DBT_MALLOC;
+ int k = htonl(99);
+ int r = db->put(db, txn, dbt_init(&key, &k, sizeof k), &val, 0);
+ assert(r==0);
+ //printf("val.data=%p\n", val.data);
+ return 0;
+}
+static void *startb(void* ignore __attribute__((__unused__))) {
+ DB_TXN *txn = 0;
+ DBT key, val;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+ int k = htonl(0);
+ val.flags |= DB_DBT_MALLOC;
+ int r = db->get(db, txn, dbt_init(&key, &k, sizeof k), &val, 0);
+ assert(r==0);
+ //printf("val.data=%p\n", val.data);
+ int i; for (i=0; i<10; i++) assert(((char*)val.data)[i]==0);
+ toku_free(val.data);
+ return 0;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ pthread_t a,b;
+ initialize();
+ { int x = pthread_create(&a, NULL, starta, NULL); assert(x==0); }
+ { int x = pthread_create(&b, NULL, startb, NULL); assert(x==0); }
+ { int x = pthread_join(a, NULL); assert(x==0); }
+ { int x = pthread_join(b, NULL); assert(x==0); }
+ finish();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hot-optimize-table-tests.cc b/storage/tokudb/PerconaFT/src/tests/hot-optimize-table-tests.cc
new file mode 100644
index 00000000000..f0fe3c9043f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hot-optimize-table-tests.cc
@@ -0,0 +1,239 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// hot-optimize-table-tests.c
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL |
+ DB_CREATE |
+ DB_THREAD |
+ DB_INIT_LOCK |
+ DB_INIT_LOG |
+ DB_INIT_TXN |
+ DB_PRIVATE;
+
+DB_ENV* env;
+unsigned int leaf_hits;
+
+// Custom Update Function for our test FT.
+static int
+update_func(DB* UU(db),
+ const DBT* key,
+ const DBT* old_val,
+ const DBT* extra,
+ void (*set_val)(const DBT* new_val, void* set_extra) __attribute__((unused)),
+ void* UU(set_extra))
+{
+ unsigned int *x_results;
+ assert(extra->size == sizeof x_results);
+ x_results = *(unsigned int **) extra->data;
+ assert(x_results);
+ assert(old_val->size > 0);
+ unsigned int* indexptr;
+ assert(key->size == (sizeof *indexptr));
+ indexptr = (unsigned int*)key->data;
+ ++leaf_hits;
+
+ if (verbose && x_results[*indexptr] != 0) {
+ printf("x_results = %p, indexptr = %p, *indexptr = %u, x_results[*indexptr] = %u\n", x_results, indexptr, *indexptr, x_results[*indexptr]);
+ }
+
+ assert(x_results[*indexptr] == 0);
+ x_results[*indexptr]++;
+ // ++(x_results[*indexptr]);
+ // memset(&new_val, 0, sizeof(new_val));
+ // set_val(&new_val, set_extra);
+ unsigned int i = *indexptr;
+ if (verbose && ((i + 1) % 50000 == 0)) {
+ printf("applying update to %u\n", i);
+ //printf("x_results[] = %u\n", x_results[*indexptr]);
+ }
+
+ return 0;
+}
+
+///
+static void
+hot_test_setup(void)
+{
+ int r = 0;
+ // Remove any previous environment.
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+
+ // Set up a new environment.
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp);CKERR(r);
+ env->set_update(env, update_func);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void
+hot_test_destroy(void)
+{
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+///
+static void
+hot_insert_keys(DB* db, unsigned int key_count)
+{
+ int r = 0;
+ DB_TXN * xact;
+ unsigned int limit = 1;
+ if (key_count > 10) {
+ limit = 100000;
+ }
+
+ // Dummy data.
+ const unsigned int DUMMY_SIZE = 100;
+ size_t size = DUMMY_SIZE;
+ char* dummy = NULL;
+ dummy = (char*)toku_xmalloc(size);
+ memset(dummy, 0, size);
+
+ // Start the transaction for insertions.
+ //
+ r = env->txn_begin(env, 0, &xact, 0); CKERR(r);
+
+ unsigned int key;
+
+ DBT key_thing;
+ DBT *keyptr = dbt_init(&key_thing, &key, sizeof(key));
+ DBT value_thing;
+ DBT *valueptr = dbt_init(&value_thing, dummy, size);
+ for (key = 0; key < key_count; ++key)
+ {
+ { int chk_r = db->put(db, xact, keyptr, valueptr, 0); CKERR(chk_r); }
+
+ // DEBUG OUTPUT
+ //
+ if (verbose && (key + 1) % limit == 0) {
+ printf("%d Elements inserted.\n", key + 1);
+ }
+ }
+
+ // Commit the insert transaction.
+ //
+ r = xact->commit(xact, 0); CKERR(r);
+
+ toku_free(dummy);
+}
+
+///
+static void
+hot_create_db(DB** db, const char* c)
+{
+ int r = 0;
+ DB_TXN* xact;
+ verbose ? printf("Creating DB.\n") : 0;
+ r = env->txn_begin(env, 0, &xact, 0); CKERR(r);
+ { int chk_r = db_create(db, env, 0); CKERR(chk_r); }
+ { int chk_r = (*db)->open((*db), xact, c, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ r = xact->commit(xact, 0); CKERR(r);
+ verbose ? printf("DB Created.\n") : 0;
+}
+
+///
+static void
+hot_test(DB* db, unsigned int size)
+{
+ int r = 0;
+ leaf_hits = 0;
+ verbose ? printf("Insert some data.\n") : 0;
+
+ // Insert our keys to assemble the tree.
+ hot_insert_keys(db, size);
+
+ // Insert Broadcast Message.
+ verbose ? printf("Insert Broadcast Message.\n") : 0;
+ unsigned int *XMALLOC_N(size, x_results);
+ memset(x_results, 0, (sizeof x_results[0]) * size);
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, &x_results, sizeof x_results);
+ DB_TXN * xact;
+ r = env->txn_begin(env, 0, &xact, 0); CKERR(r);
+ r = db->update_broadcast(db, xact, extrap, 0); CKERR(r);
+ r = xact->commit(xact, 0); CKERR(r);
+
+ // Flatten the tree.
+ verbose ? printf("Calling hot optimize...\n") : 0;
+ uint64_t loops_run;
+ r = db->hot_optimize(db, NULL, NULL, NULL, NULL, &loops_run);
+ assert(r == 0);
+ verbose ? printf("HOT Finished!\n") : 0;
+ for (unsigned int i = 0; i < size; ++i) {
+ assert(x_results[i] == 1);
+ }
+ verbose ? printf("Leaves hit = %u\n", leaf_hits) :0;
+ toku_free(x_results);
+}
+
+///
+int
+test_main(int argc, char * const argv[])
+{
+ int r = 0;
+ default_parse_args(argc, argv);
+ hot_test_setup();
+
+ // Create and Open the Database/FT
+ DB *db = NULL;
+ const unsigned int BIG = 4000000;
+ const unsigned int SMALL = 10;
+ const unsigned int NONE = 0;
+
+ hot_create_db(&db, "none.db");
+ hot_test(db, NONE);
+ r = db->close(db, 0);
+ CKERR(r);
+ hot_create_db(&db, "small.db");
+ hot_test(db, SMALL);
+ r = db->close(db, 0);
+ CKERR(r);
+ hot_create_db(&db, "big.db");
+ hot_test(db, BIG);
+ r = db->close(db, 0);
+ CKERR(r);
+
+ hot_test_destroy();
+ verbose ? printf("Exiting Test.\n") : 0;
+ return r;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-bw.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-bw.cc
new file mode 100644
index 00000000000..82471f30e45
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-bw.cc
@@ -0,0 +1,469 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "key-val.h"
+
+toku_mutex_t put_lock;
+
+enum {NUM_INDEXER_INDEXES=1};
+static const int NUM_DBS = NUM_INDEXER_INDEXES + 1; // 1 for source DB
+static const int NUM_ROWS = 100000;
+static int num_rows;
+static const int FORWARD = 0;
+static const int BACKWARD = 1;
+typedef int Direction;
+static const int TXN_CREATE = 1;
+static const int TXN_END = 2;
+typedef int TxnWork;
+
+DB_ENV *env;
+
+/*
+ * client() is a routine intended to be run in a separate thread from index creation
+ * - it takes a client spec which describes work to be done
+ * - direction : move to ever increasing or decreasing rows
+ * - txnwork : whether a transaction should be created or closed within the client
+ * (allows client transaction to start before or during index creation,
+ * and to close during or after index creation)
+ */
+
+
+typedef struct {
+ uint32_t num; // number of rows to write
+ uint32_t start; // approximate start row
+ int offset; // offset from stride (= MAX_CLIENTS)
+ Direction dir;
+ TxnWork txnwork;
+ DB_TXN *txn;
+ DB **dbs;
+ int client_number;
+ uint32_t *flags;
+} client_spec_t, *client_spec;
+
+int client_count = 0;
+
+static void * client(void *arg)
+{
+ client_spec CAST_FROM_VOIDP(cs, arg);
+ client_count++;
+ if ( verbose ) printf("client[%d]\n", cs->client_number);
+ assert(cs->client_number < MAX_CLIENTS);
+ assert(cs->dir == FORWARD || cs->dir == BACKWARD);
+
+ int r;
+ if ( cs->txnwork & TXN_CREATE ) { r = env->txn_begin(env, NULL, &cs->txn, 0); CKERR(r); }
+
+ DBT key, val;
+ DBT dest_keys[NUM_DBS];
+ DBT dest_vals[NUM_DBS];
+ uint32_t k, v;
+ int n = cs->start;
+
+ for(int which=0;which<NUM_DBS;which++) {
+ dbt_init(&dest_keys[which], NULL, 0);
+ dest_keys[which].flags = DB_DBT_REALLOC;
+
+ dbt_init(&dest_vals[which], NULL, 0);
+ dest_vals[which].flags = DB_DBT_REALLOC;
+ }
+
+ int rr = 0;
+ int retry = 0;
+ for (uint32_t i = 0; i < cs->num; i++ ) {
+ DB_TXN *txn;
+ env->txn_begin(env, cs->txn, &txn, 0);
+ k = key_to_put(n, cs->offset);
+ v = generate_val(k, 0);
+ dbt_init(&key, &k, sizeof(k));
+ dbt_init(&val, &v, sizeof(v));
+
+ while ( retry++ < 10 ) {
+ toku_mutex_lock(&put_lock);
+ rr = env_put_multiple_test_no_array(env,
+ cs->dbs[0],
+ txn,
+ &key,
+ &val,
+ NUM_DBS,
+ cs->dbs, // dest dbs
+ dest_keys,
+ dest_vals,
+ cs->flags);
+ toku_mutex_unlock(&put_lock);
+ if ( rr == 0 ) break;
+ sleep(0);
+ }
+ if ( rr != 0 ) {
+ if ( verbose ) printf("client[%u] : put_multiple returns %d, i=%u, n=%u, key=%u\n", cs->client_number, rr, i, n, k);
+ r = txn->abort(txn); CKERR(r);
+ break;
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+ n = ( cs->dir == FORWARD ) ? n + 1 : n - 1;
+ retry = 0;
+ }
+
+ if ( cs->txnwork & TXN_END ) { r = cs->txn->commit(cs->txn, DB_TXN_SYNC); CKERR(r); }
+ if (verbose) printf("client[%d] done\n", cs->client_number);
+
+ for (int which=0; which<NUM_DBS; which++) {
+ toku_free(dest_keys[which].data);
+ toku_free(dest_vals[which].data);
+ }
+
+ return 0;
+}
+
+toku_pthread_t *client_threads;
+client_spec_t *client_specs;
+
+static void clients_init(DB **dbs, uint32_t *flags)
+{
+ XMALLOC_N(MAX_CLIENTS, client_threads);
+ XMALLOC_N(MAX_CLIENTS, client_specs);
+
+ client_specs[0].client_number = 0;
+// client_specs[0].start = 0;
+ client_specs[0].start = num_rows - 1;
+ client_specs[0].num = num_rows;
+ client_specs[0].offset = -1;
+// client_specs[0].dir = FORWARD;
+ client_specs[0].dir = BACKWARD;
+ client_specs[0].txnwork = TXN_CREATE | TXN_END;
+ client_specs[0].txn = NULL;
+ client_specs[0].dbs = dbs;
+ client_specs[0].flags = flags;
+
+ client_specs[1].client_number = 1;
+ client_specs[1].start = 0;
+ client_specs[1].num = num_rows;
+ client_specs[1].offset = 1;
+ client_specs[1].dir = FORWARD;
+ client_specs[1].txnwork = TXN_CREATE | TXN_END;
+ client_specs[1].txn = NULL;
+ client_specs[1].dbs = dbs;
+ client_specs[1].flags = flags;
+
+}
+
+static void clients_cleanup(void)
+{
+ toku_free(client_threads); client_threads = NULL;
+ toku_free(client_specs); client_specs = NULL;
+}
+
+// verify results
+// - read the keys in the primary table, then calculate what keys should exist
+// in the other DB. Read the other table to verify.
+static void check_results(DB *src, DB *db)
+{
+ int r;
+ int pass = 1;
+
+ int clients = client_count;
+
+ int max_rows = ( clients + 1 ) * num_rows;
+ unsigned int *db_keys = (unsigned int *) toku_malloc(max_rows * sizeof (unsigned int));
+
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DBC *cursor;
+ r = src->cursor(src, txn, &cursor, 0); CKERR(r);
+
+ int which = *(uint32_t*)db->app_private;
+
+ // scan the primary table,
+ // calculate the expected keys in 'db'
+ int row = 0;
+ while ( r != DB_NOTFOUND ) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if ( r != DB_NOTFOUND ) {
+ k = *((uint32_t *)(key.data));
+ db_keys[row] = twiddle32(k, which);
+ row++;
+ }
+ }
+ if ( verbose ) printf("primary table scanned, contains %d rows\n", row);
+ int primary_rows = row;
+ r = cursor->c_close(cursor); CKERR(r);
+ // sort the expected keys
+ qsort(db_keys, primary_rows, sizeof (unsigned int), uint_cmp);
+
+ if ( verbose > 1 ) {
+ for(int i=0;i<primary_rows;i++) {
+ printf("primary table[%u] = %u\n", i, db_keys[i]);
+ }
+ }
+
+ // scan the indexer-created DB, comparing keys with expected keys
+ // - there should be exactly 'primary_rows' in the new index
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ for (int i=0;i<primary_rows;i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if ( r == DB_NOTFOUND ) {
+ printf("scan of index finds last row is %d\n", i);
+ }
+ CKERR(r);
+ k = *((uint32_t *)(key.data));
+ if ( db_keys[i] != k ) {
+ if ( verbose ) printf("ERROR expecting key %10u for row %d, found key = %10u\n", db_keys[i],i,k);
+ pass = 0;
+ i++;
+// goto check_results_error;
+ }
+ }
+ // next cursor op should return DB_NOTFOUND
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ assert(r == DB_NOTFOUND);
+
+ // we're done - cleanup and close
+//check_results_error:
+ r = cursor->c_close(cursor); CKERR(r);
+ toku_free(db_keys);
+ r = txn->commit(txn, 0); CKERR(r);
+ if ( verbose ) {
+ if ( pass ) printf("check_results : pass\n");
+ else printf("check_results : fail\n");
+ }
+ assert(pass);
+ return;
+}
+
+static void test_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = 0;
+ }
+ clients_init(dbs, db_flags);
+
+ // create and initialize indexer
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer create_indexer\n");
+ toku_mutex_lock(&put_lock);
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS-1, &dbs[1], db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+ toku_mutex_unlock(&put_lock);
+
+ // start threads doing additional inserts - no lock issues since indexer already created
+ r = toku_pthread_create(&client_threads[0], 0, client, (void *)&client_specs[0]); CKERR(r);
+// r = toku_pthread_create(&client_threads[1], 0, client, (void *)&client_specs[1]); CKERR(r);
+
+ struct timeval start, now;
+ if ( verbose ) {
+ printf("test_indexer build\n");
+ gettimeofday(&start,0);
+ }
+ r = indexer->build(indexer);
+ CKERR(r);
+ if ( verbose ) {
+ gettimeofday(&now,0);
+ int duration = (int)(now.tv_sec - start.tv_sec);
+ if ( duration > 0 )
+ printf("test_indexer build : sec = %d\n", duration);
+ }
+
+ if ( verbose ) printf("test_indexer close\n");
+ toku_mutex_lock(&put_lock);
+ r = indexer->close(indexer);
+ CKERR(r);
+ toku_mutex_unlock(&put_lock);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ void *t0;
+ r = toku_pthread_join(client_threads[0], &t0); CKERR(r);
+// void *t1;
+// r = toku_pthread_join(client_threads[1], &t1); CKERR(r);
+
+ clients_cleanup();
+
+ if ( verbose ) printf("check_results\n");
+ check_results(src, dbs[1]);
+
+ if ( verbose ) printf("PASS\n");
+ if ( verbose ) printf("test_indexer done\n");
+}
+
+
+static void run_test(void)
+{
+ int r;
+ toku_mutex_init(&put_lock, NULL);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ generate_permute_tables();
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ int ids[MAX_DBS];
+ DB *dbs[MAX_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ ids[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &ids[i];
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ // generate the src DB (do not use put_multiple)
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = generate_initial_table(dbs[0], txn, num_rows); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ // -------------------------- //
+ if (1) test_indexer(dbs[0], dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+ toku_mutex_destroy(&put_lock);
+ r = env->close(env, 0); CKERR(r);
+}
+
+// ------------ infrastructure ----------
+
+static inline void
+do_args (int argc, char * const argv[]) {
+ const char *progname=argv[0];
+ num_rows = NUM_ROWS;
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0],"-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose=0;
+ } else if (strcmp(argv[0],"-r")==0) {
+ argc--; argv++;
+ num_rows = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Usage:\n %s [-v] [-q] [-r rows]\n", progname);
+ exit(1);
+ }
+ argc--; argv++;
+ }
+}
+
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+
+/*
+ * Please ignore this code - I don't think I'm going to use it, but I don't want to lose it
+ * I will delete this later - Dave
+
+ if ( rr != 0 ) { // possible lock deadlock
+ if (verbose > 1) {
+ printf("client[%u] : put_multiple returns %d, i=%u, n=%u, key=%u\n", cs->client_number, rr, i, n, k);
+ if ( verbose > 2 ) print_engine_status(env);
+ }
+ // abort the transaction, freeing up locks associated with previous put_multiples
+ if ( verbose > 1 ) printf("start txn abort\n");
+ r = txn->abort(txn); CKERR(r);
+ if ( verbose > 1 ) printf(" txn aborted\n");
+ sleep(2 + cs->client_number);
+ // now retry, waiting until the deadlock resolves itself
+ r = env->txn_begin(env, cs->txn, &txn, 0); CKERR(r);
+ if ( verbose > 1 ) printf("txn begin\n");
+ while ( rr != 0 ) {
+ rr = env->put_multiple(env,
+ cs->dbs[0],
+ txn,
+ &key,
+ &val,
+ NUM_DBS,
+ cs->dbs, // dest dbs
+ dest_keys,
+ dest_vals,
+ cs->flags,
+ NULL);
+ if ( rr != 0 ) {
+ if ( verbose ) printf("client[%u] : put_multiple returns %d, i=%u, n=%u, key=%u\n", cs->client_number, rr, i, n, k);
+ if ( verbose ) printf("start txn abort\n");
+ r = txn->abort(txn); CKERR(r);
+ if ( verbose ) printf(" txn aborted\n");
+ sleep(2 + cs->client_number);
+ r = env->txn_begin(env, cs->txn, &txn, 0); CKERR(r);
+ if ( verbose ) printf("txn begin\n");
+ }
+ }
+ */
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-error-callback.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-error-callback.cc
new file mode 100644
index 00000000000..97e136eacb2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-error-callback.cc
@@ -0,0 +1,172 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "key-val.h"
+#include "ydb.h"
+#include "indexer.h"
+
+enum {NUM_DBS=1};
+static const int NUM_ROWS = 10;
+typedef enum {FORWARD = 0, BACKWARD} Direction;
+typedef enum {TXN_NONE = 0, TXN_CREATE = 1, TXN_END = 2} TxnWork;
+
+DB_ENV *env;
+
+int error_cb_count = 0;
+static void error_callback(DB *db, int which_db, int err, DBT *key, DBT *val, void *extra)
+{
+ error_cb_count++;
+ if ( verbose ) {
+ printf("error_callback (%d) : db_p = %p, which_db = %d, error = %d, key_p = %p, val_p = %p, extra_p = %p\n",
+ error_cb_count,
+ db, which_db,
+ err,
+ key, val, extra);
+ }
+}
+
+static void test_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ }
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer create_indexer\n");
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS, dbs, db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, error_callback, NULL);
+ CKERR(r);
+ toku_indexer_set_test_only_flags(indexer, INDEXER_TEST_ONLY_ERROR_CALLBACK);
+
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ r = indexer->build(indexer);
+ assert(r != 0 ); // build should return an error
+ assert(error_cb_count == 1); // error callback count should be 1
+
+ if ( verbose ) printf("test_indexer close\n");
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ if ( verbose ) printf("PASS\n");
+ if ( verbose ) printf("test_indexer done\n");
+}
+
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+// r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_default_bt_compare(env, int_dbt_cmp); CKERR(r);
+ generate_permute_tables();
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DB *src_db = NULL;
+ const char *src_name="src.db";
+ r = db_create(&src_db, env, 0); CKERR(r);
+ r = src_db->open(src_db, NULL, src_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = generate_initial_table(src_db, txn, NUM_ROWS); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+
+ DB *dbs[NUM_DBS];
+ int idx[MAX_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ idx[i] = i+1;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ // -------------------------- //
+ if (1) test_indexer(src_db, dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = src_db->close(src_db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+// ------------ infrastructure ----------
+
+int test_main(int argc, char * const argv[]) {
+ default_parse_args(argc, argv);
+ run_test();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-committed-optimized.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-committed-optimized.cc
new file mode 100644
index 00000000000..0ffc57dc0b2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-committed-optimized.cc
@@ -0,0 +1,183 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include "key-val.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+enum {NUM_DBS=1};
+enum {NUM_KV_PAIRS=3};
+struct kv_pair {
+ int64_t key;
+ int64_t val;
+};
+struct kv_pair kv_pairs[NUM_KV_PAIRS] = {{1,4},
+ {2,5},
+ {3,6}};
+
+static void test_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ }
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer create_indexer\n");
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS, dbs, db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer build\n");
+ r = indexer->build(indexer);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer close\n");
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ if ( verbose ) printf("PASS\n");
+ if ( verbose ) printf("test_indexer done\n");
+}
+
+const char *src_name="src.db";
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate_switch); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); CKERR(r);
+ r = src_db->open(src_db, NULL, src_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT key, val;
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ dbt_init(&key, &kv_pairs[i].key, sizeof(kv_pairs[i].key));
+ dbt_init(&val, &kv_pairs[i].val, sizeof(kv_pairs[i].val));
+ r = src_db->put(src_db, txn, &key, &val, 0); CKERR(r);
+ }
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ r = src_db->optimize(src_db); CKERR(r);
+
+ DB *dbs[NUM_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ dbs[i]->app_private = (void *) (intptr_t) i;
+ }
+
+ // -------------------------- //
+ if (1) test_indexer(src_db, dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = src_db->close(src_db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s\n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-committed.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-committed.cc
new file mode 100644
index 00000000000..21b4ecfc09e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-committed.cc
@@ -0,0 +1,181 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include "key-val.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+enum {NUM_DBS=1};
+enum {NUM_KV_PAIRS=3};
+struct kv_pair {
+ int64_t key;
+ int64_t val;
+};
+struct kv_pair kv_pairs[NUM_KV_PAIRS] = {{1,4},
+ {2,5},
+ {3,6}};
+
+static void test_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ }
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer create_indexer\n");
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS, dbs, db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer build\n");
+ r = indexer->build(indexer);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer close\n");
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ if ( verbose ) printf("PASS\n");
+ if ( verbose ) printf("test_indexer done\n");
+}
+
+const char *src_name="src.db";
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate_switch); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); CKERR(r);
+ r = src_db->open(src_db, NULL, src_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT key, val;
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ dbt_init(&key, &kv_pairs[i].key, sizeof(kv_pairs[i].key));
+ dbt_init(&val, &kv_pairs[i].val, sizeof(kv_pairs[i].val));
+ r = src_db->put(src_db, txn, &key, &val, 0); CKERR(r);
+ }
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ DB *dbs[NUM_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ dbs[i]->app_private = (void *) (intptr_t) i;
+ }
+
+ // -------------------------- //
+ if (1) test_indexer(src_db, dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = src_db->close(src_db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s\n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-provisional.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-provisional.cc
new file mode 100644
index 00000000000..aa1173ef75c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-insert-provisional.cc
@@ -0,0 +1,182 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include "key-val.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+enum {NUM_DBS=1};
+enum {NUM_KV_PAIRS=3};
+struct kv_pair {
+ int64_t key;
+ int64_t val;
+};
+struct kv_pair kv_pairs[NUM_KV_PAIRS] = {{1,4},
+ {2,5},
+ {3,6}};
+
+static void test_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ }
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer create_indexer\n");
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS, dbs, db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer build\n");
+ r = indexer->build(indexer);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer close\n");
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ if ( verbose ) printf("PASS\n");
+ if ( verbose ) printf("test_indexer done\n");
+}
+
+const char *src_name="src.db";
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate_switch); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); CKERR(r);
+ r = src_db->open(src_db, NULL, src_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT key, val;
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ dbt_init(&key, &kv_pairs[i].key, sizeof(kv_pairs[i].key));
+ dbt_init(&val, &kv_pairs[i].val, sizeof(kv_pairs[i].val));
+ r = src_db->put(src_db, txn, &key, &val, 0); CKERR(r);
+ }
+
+ DB *dbs[NUM_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ dbs[i]->app_private = (void *) (intptr_t) i;
+ }
+
+ // -------------------------- //
+ if (1) test_indexer(src_db, dbs);
+ // -------------------------- //
+
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = src_db->close(src_db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s\n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-lock-test.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-lock-test.cc
new file mode 100644
index 00000000000..c06a06dcbde
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-lock-test.cc
@@ -0,0 +1,220 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include "key-val.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+enum {NUM_DBS=1};
+enum {NUM_KV_PAIRS=3};
+struct kv_pair {
+ int64_t key;
+ int64_t val;
+};
+struct kv_pair kv_pairs[NUM_KV_PAIRS] = {{1,4},
+ {2,5},
+ {3,6}};
+
+static void run_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ }
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("run_indexer create_indexer\n");
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS, dbs, db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ if ( verbose ) printf("run_indexer build\n");
+ r = indexer->build(indexer);
+ CKERR(r);
+
+ if ( verbose ) printf("run_indexer close\n");
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ if ( verbose ) printf("run_indexer done\n");
+}
+
+const char *src_name="src.db";
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate_switch); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); CKERR(r);
+ r = src_db->open(src_db, NULL, src_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT key, val;
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ dbt_init(&key, &kv_pairs[i].key, sizeof(kv_pairs[i].key));
+ dbt_init(&val, &kv_pairs[i].val, sizeof(kv_pairs[i].val));
+ r = src_db->put(src_db, txn, &key, &val, 0); CKERR(r);
+ }
+
+ DB *dbs[NUM_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ dbs[i]->app_private = (void *) (intptr_t) i;
+ }
+
+ run_indexer(src_db, dbs);
+
+ // at this point the hot dictionary should have locks on the rows since the transaction
+ // that created the src dictionary is still open
+
+ // try overwriting a value in hot dictionary[0]
+ {
+ DB_TXN *owrt_txn;
+ r = env->txn_begin(env, NULL, &owrt_txn, 0);
+ CKERR(r);
+
+ dbt_init(&key, &kv_pairs[0].key, sizeof(kv_pairs[0].key));
+ dbt_init(&key, &kv_pairs[0].val, sizeof(kv_pairs[0].val));
+ r = dbs[0]->put(dbs[0], owrt_txn, &key, &val, 0);
+
+ assert(r == DB_LOCK_NOTGRANTED );
+ if ( verbose ) printf("lock contention detected, as expected ( put returns DB_LOCK_NOTGRANTED )\n");
+
+ r = owrt_txn->commit(owrt_txn, DB_TXN_SYNC);
+ CKERR(r);
+ }
+
+ // close the transaction (releasing locks), and try writing again
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+ {
+ DB_TXN *owrt_txn;
+ r = env->txn_begin(env, NULL, &owrt_txn, 0);
+ CKERR(r);
+
+ dbt_init(&key, &kv_pairs[0].key, sizeof(kv_pairs[0].key));
+ dbt_init(&key, &kv_pairs[0].val, sizeof(kv_pairs[0].val));
+ r = dbs[0]->put(dbs[0], owrt_txn, &key, &val, 0);
+
+ assert(r == 0 );
+ if ( verbose ) printf("no lock contention detected, as expected ( put returns 0 )\n");
+
+ r = owrt_txn->commit(owrt_txn, DB_TXN_SYNC);
+ CKERR(r);
+ }
+
+
+ if ( verbose ) printf("PASS\n");
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = src_db->close(src_db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s\n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-multiclient.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-multiclient.cc
new file mode 100644
index 00000000000..eefc621f80a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-multiclient.cc
@@ -0,0 +1,466 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "key-val.h"
+
+enum {NUM_INDEXER_INDEXES=1};
+static const int NUM_DBS = NUM_INDEXER_INDEXES + 1; // 1 for source DB
+static const int NUM_ROWS = 10000;
+int num_rows;
+typedef enum {FORWARD = 0, BACKWARD} Direction;
+typedef enum {TXN_NONE = 0, TXN_CREATE = 1, TXN_END = 2} TxnWork;
+
+DB_ENV *env;
+
+/*
+ * client() is a routine intended to be run in a separate thread from index creation
+ * - it takes a client spec which describes work to be done
+ * - direction : move to ever increasing or decreasing rows
+ * - txnwork : whether a transaction should be created or closed within the client
+ * (allows client transaction to start before or during index creation,
+ * and to close during or after index creation)
+ */
+
+typedef struct {
+ uint32_t num; // number of rows to write
+ uint32_t start; // approximate start row
+ int offset; // offset from stride (= MAX_CLIENTS)
+ Direction dir;
+ int txnwork;
+ DB_TXN *txn;
+ uint32_t max_inserts_per_txn; // this is for the parent transaction
+ DB **dbs;
+ int client_number;
+ uint32_t *flags;
+} client_spec_t, *client_spec;
+
+int client_count = 0;
+
+static void * client(void *arg)
+{
+ client_spec CAST_FROM_VOIDP(cs, arg);
+ client_count++;
+ if ( verbose ) printf("client[%d]\n", cs->client_number);
+ assert(cs->client_number < MAX_CLIENTS);
+ assert(cs->dir == FORWARD || cs->dir == BACKWARD);
+
+ int r;
+ if ( cs->txnwork & TXN_CREATE ) { r = env->txn_begin(env, NULL, &cs->txn, 0); CKERR(r); }
+
+ DBT key, val;
+ DBT dest_keys[NUM_DBS];
+ DBT dest_vals[NUM_DBS];
+ uint32_t k, v;
+ int n = cs->start;
+
+ for(int which=0;which<NUM_DBS;which++) {
+ dbt_init(&dest_keys[which], NULL, 0);
+ dest_keys[which].flags = DB_DBT_REALLOC;
+
+ dbt_init(&dest_vals[which], NULL, 0);
+ dest_vals[which].flags = DB_DBT_REALLOC;
+ }
+
+ int rr;
+ uint32_t inserts = 0;
+ for (uint32_t i = 0; i < cs->num; i++ ) {
+ DB_TXN *txn;
+ env->txn_begin(env, cs->txn, &txn, 0);
+ k = key_to_put(n, cs->offset);
+ v = generate_val(k, 0);
+ dbt_init(&key, &k, sizeof(k));
+ dbt_init(&val, &v, sizeof(v));
+
+ rr = env_put_multiple_test_no_array(env,
+ cs->dbs[0],
+ txn,
+ &key,
+ &val,
+ NUM_DBS,
+ cs->dbs, // dest dbs
+ dest_keys,
+ dest_vals,
+ cs->flags);
+ if ( rr != 0 ) {
+ if ( verbose ) printf("client[%u] : put_multiple returns %d, i=%u, n=%u, key=%u\n", cs->client_number, rr, i, n, k);
+ r = txn->abort(txn); CKERR(r);
+ break;
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+ // limit the number of inserts per parent transaction to prevent lock escalation
+ inserts++;
+ if ( inserts >= cs->max_inserts_per_txn ) {
+ r = cs->txn->commit(cs->txn, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &cs->txn, 0); CKERR(r);
+ inserts = 0;
+ }
+ n = ( cs->dir == FORWARD ) ? n + 1 : n - 1;
+ }
+
+ if ( cs->txnwork & TXN_END ) { r = cs->txn->commit(cs->txn, DB_TXN_SYNC); CKERR(r); }
+ if (verbose) printf("client[%d] done\n", cs->client_number);
+
+ for (int which=0; which<NUM_DBS; which++) {
+ toku_free(dest_keys[which].data);
+ toku_free(dest_vals[which].data);
+ }
+
+
+ return 0;
+}
+
+toku_pthread_t *client_threads;
+client_spec_t *client_specs;
+
+static void clients_init(DB **dbs, uint32_t *flags)
+{
+ XMALLOC_N(MAX_CLIENTS, client_threads);
+ XMALLOC_N(MAX_CLIENTS, client_specs);
+
+ client_specs[0].client_number = 0;
+ client_specs[0].start = 0;
+ client_specs[0].num = num_rows;
+ client_specs[0].offset = -1;
+ client_specs[0].dir = FORWARD;
+ client_specs[0].txnwork = TXN_CREATE | TXN_END;
+ client_specs[0].txn = NULL;
+ client_specs[0].max_inserts_per_txn = 1000;
+ client_specs[0].dbs = dbs;
+ client_specs[0].flags = flags;
+
+ client_specs[1].client_number = 1;
+ client_specs[1].start = 0;
+ client_specs[1].num = num_rows;
+ client_specs[1].offset = 1;
+ client_specs[1].dir = FORWARD;
+ client_specs[1].txnwork = TXN_CREATE | TXN_END;
+ client_specs[1].txn = NULL;
+ client_specs[1].max_inserts_per_txn = 100;
+ client_specs[1].dbs = dbs;
+ client_specs[1].flags = flags;
+
+ client_specs[2].client_number = 2;
+ client_specs[2].start = num_rows -1;
+ client_specs[2].num = num_rows;
+ client_specs[2].offset = -2;
+ client_specs[2].dir = BACKWARD;
+ client_specs[2].txnwork = TXN_CREATE | TXN_END;
+ client_specs[2].txn = NULL;
+ client_specs[2].max_inserts_per_txn = 1000;
+ client_specs[2].dbs = dbs;
+ client_specs[2].flags = flags;
+}
+
+static void clients_cleanup(void)
+{
+ toku_free(client_threads); client_threads = NULL;
+ toku_free(client_specs); client_specs = NULL;
+}
+
+// verify results
+// - read the keys in the primary table, then calculate what keys should exist
+// in the other DB. Read the other table to verify.
+static int check_results(DB *src, DB *db)
+{
+ int r;
+ int fail = 0;
+
+ int clients = client_count;
+
+ int max_rows = ( clients + 1 ) * num_rows;
+ unsigned int *db_keys = (unsigned int *) toku_malloc(max_rows * sizeof (unsigned int));
+
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DBC *cursor;
+ r = src->cursor(src, txn, &cursor, 0); CKERR(r);
+
+ int which = *(uint32_t*)db->app_private;
+
+ // scan the primary table,
+ // calculate the expected keys in 'db'
+ int row = 0;
+ while ( r != DB_NOTFOUND ) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if ( r != DB_NOTFOUND ) {
+ k = *((uint32_t *)(key.data));
+ db_keys[row] = twiddle32(k, which);
+ row++;
+ }
+ }
+ if ( verbose ) printf("primary table scanned, contains %d rows\n", row);
+ int primary_rows = row;
+ r = cursor->c_close(cursor); CKERR(r);
+ // sort the expected keys
+ qsort(db_keys, primary_rows, sizeof (unsigned int), uint_cmp);
+
+ if ( verbose > 1 ) {
+ for(int i=0;i<primary_rows;i++) {
+ printf("primary table[%u] = %u\n", i, db_keys[i]);
+ }
+ }
+
+ // scan the indexer-created DB, comparing keys with expected keys
+ // - there should be exactly 'primary_rows' in the new index
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ for (int i=0;i<primary_rows;i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if ( r == DB_NOTFOUND ) {
+ printf("scan of index finds last row is %d\n", i);
+ }
+ CKERR(r);
+ k = *((uint32_t *)(key.data));
+ if ( db_keys[i] != k ) {
+ if ( verbose ) printf("ERROR expecting key %10u for row %d, found key = %10u\n", db_keys[i],i,k);
+ fail = 1;
+ goto check_results_error;
+ }
+ }
+ // next cursor op should return DB_NOTFOUND
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ assert(r == DB_NOTFOUND);
+
+ // we're done - cleanup and close
+check_results_error:
+ r = cursor->c_close(cursor); CKERR(r);
+ toku_free(db_keys);
+ r = txn->commit(txn, 0); CKERR(r);
+ if ( verbose ) {
+ if ( fail ) printf("check_results : fail\n");
+ else printf("check_results : pass\n");
+ }
+ return fail;
+}
+
+static void test_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = 0;
+ }
+ clients_init(dbs, db_flags);
+
+ // create and initialize indexer
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer create_indexer\n");
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS-1, &dbs[1], db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ // start threads doing additional inserts - no lock issues since indexer already created
+ r = toku_pthread_create(&client_threads[0], 0, client, (void *)&client_specs[0]); CKERR(r);
+ r = toku_pthread_create(&client_threads[1], 0, client, (void *)&client_specs[1]); CKERR(r);
+// r = toku_pthread_create(&client_threads[2], 0, client, (void *)&client_specs[2]); CKERR(r);
+
+ struct timeval start, now;
+ if ( verbose ) {
+ printf("test_indexer build\n");
+ gettimeofday(&start,0);
+ }
+ r = indexer->build(indexer);
+ CKERR(r);
+ if ( verbose ) {
+ gettimeofday(&now,0);
+ int duration = (int)(now.tv_sec - start.tv_sec);
+ if ( duration > 0 )
+ printf("test_indexer build : sec = %d\n", duration);
+ }
+
+ void *t0; r = toku_pthread_join(client_threads[0], &t0); CKERR(r);
+ void *t1; r = toku_pthread_join(client_threads[1], &t1); CKERR(r);
+// void *t2; r = toku_pthread_join(client_threads[2], &t2); CKERR(r);
+
+ if ( verbose ) printf("test_indexer close\n");
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ clients_cleanup();
+
+ if ( verbose ) printf("check_results\n");
+ r = check_results(src, dbs[1]);
+ CKERR(r);
+
+ if ( verbose && (r == 0)) printf("PASS\n");
+ if ( verbose && (r == 0)) printf("test_indexer done\n");
+}
+
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ generate_permute_tables();
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ int ids[MAX_DBS];
+ DB *dbs[MAX_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ ids[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &ids[i];
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ // generate the src DB (do not use put_multiple)
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = generate_initial_table(dbs[0], txn, num_rows); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ // -------------------------- //
+ if (1) test_indexer(dbs[0], dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+ r = env->close(env, 0); CKERR(r);
+}
+
+// ------------ infrastructure ----------
+
+static inline void
+do_args (int argc, char * const argv[]) {
+ const char *progname=argv[0];
+ num_rows = NUM_ROWS;
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0],"-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose=0;
+ } else if (strcmp(argv[0],"-r")==0) {
+ argc--; argv++;
+ num_rows = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Usage:\n %s [-v] [-q] [-r rows]\n", progname);
+ exit(1);
+ }
+ argc--; argv++;
+ }
+}
+
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+
+/*
+ * Please ignore this code - I don't think I'm going to use it, but I don't want to lose it
+ * I will delete this later - Dave
+
+ if ( rr != 0 ) { // possible lock deadlock
+ if (verbose > 1) {
+ printf("client[%u] : put_multiple returns %d, i=%u, n=%u, key=%u\n", cs->client_number, rr, i, n, k);
+ if ( verbose > 2 ) print_engine_status(env);
+ }
+ // abort the transaction, freeing up locks associated with previous put_multiples
+ if ( verbose > 1 ) printf("start txn abort\n");
+ r = txn->abort(txn); CKERR(r);
+ if ( verbose > 1 ) printf(" txn aborted\n");
+ sleep(2 + cs->client_number);
+ // now retry, waiting until the deadlock resolves itself
+ r = env->txn_begin(env, cs->txn, &txn, 0); CKERR(r);
+ if ( verbose > 1 ) printf("txn begin\n");
+ while ( rr != 0 ) {
+ rr = env->put_multiple(env,
+ cs->dbs[0],
+ txn,
+ &key,
+ &val,
+ NUM_DBS,
+ cs->dbs, // dest dbs
+ dest_keys,
+ dest_vals,
+ cs->flags,
+ NULL);
+ if ( rr != 0 ) {
+ if ( verbose ) printf("client[%u] : put_multiple returns %d, i=%u, n=%u, key=%u\n", cs->client_number, rr, i, n, k);
+ if ( verbose ) printf("start txn abort\n");
+ r = txn->abort(txn); CKERR(r);
+ if ( verbose ) printf(" txn aborted\n");
+ sleep(2 + cs->client_number);
+ r = env->txn_begin(env, cs->txn, &txn, 0); CKERR(r);
+ if ( verbose ) printf("txn begin\n");
+ }
+ }
+ */
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-nested-insert-committed.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-nested-insert-committed.cc
new file mode 100644
index 00000000000..25b01728350
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-nested-insert-committed.cc
@@ -0,0 +1,187 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include "key-val.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+enum {NUM_DBS=1};
+enum {NUM_KV_PAIRS=3};
+struct kv_pair {
+ int64_t key;
+ int64_t val;
+};
+struct kv_pair kv_pairs[NUM_KV_PAIRS] = {{1,4},
+ {2,5},
+ {3,6}};
+
+static void test_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ }
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer create_indexer\n");
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS, dbs, db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer build\n");
+ r = indexer->build(indexer);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer close\n");
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ if ( verbose ) printf("PASS\n");
+ if ( verbose ) printf("test_indexer done\n");
+}
+
+const char *src_name="src.db";
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate_switch); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); CKERR(r);
+ r = src_db->open(src_db, NULL, src_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT key, val;
+
+ DB_TXN *txn0 = NULL;
+ r = env->txn_begin(env, NULL, &txn0, 0); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, txn0, &txn, 0); CKERR(r);
+
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ dbt_init(&key, &kv_pairs[i].key, sizeof(kv_pairs[i].key));
+ dbt_init(&val, &kv_pairs[i].val, sizeof(kv_pairs[i].val));
+ r = src_db->put(src_db, txn, &key, &val, 0); CKERR(r);
+ }
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+ r = txn0->commit(txn0, DB_TXN_SYNC); CKERR(r);
+
+ DB *dbs[NUM_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ dbs[i]->app_private = (void *) (intptr_t) i;
+ }
+
+ // -------------------------- //
+ if (1) test_indexer(src_db, dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = src_db->close(src_db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s\n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-put-abort.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-put-abort.cc
new file mode 100644
index 00000000000..27501291b15
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-put-abort.cc
@@ -0,0 +1,188 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "ydb.h"
+#include "toku_pthread.h"
+
+// this test reproduces the rollback log corruption that occurs when hot indexing runs concurrent with a long abort
+// the concurrent operation occurs when the abort periodically releases the ydb lock which allows the hot indexer
+// to run. the hot indexer erroneously append to the rollback log that is in the process of being aborted.
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ (void) dest_db; (void) src_db; (void) dest_keys; (void) dest_vals; (void) src_key; (void) src_val;
+ lazy_assert(src_db != NULL && dest_db != NULL);
+
+ if (dest_key->flags == DB_DBT_REALLOC) {
+ dest_key->data = toku_realloc(dest_key->data, src_val->size);
+ memcpy(dest_key->data, src_val->data, src_val->size);
+ dest_key->size = src_val->size;
+ }
+ dest_val->size = 0;
+
+ return 0;
+}
+
+struct indexer_arg {
+ DB_ENV *env;
+ DB *src_db;
+ int n_dest_db;
+ DB **dest_db;
+};
+
+static void *
+indexer_thread(void *arg) {
+ struct indexer_arg *indexer_arg = (struct indexer_arg *) arg;
+ DB_ENV *env = indexer_arg->env;
+ int r;
+
+ DB_TXN *indexer_txn = NULL;
+ r = env->txn_begin(env, NULL, &indexer_txn, 0); assert_zero(r);
+
+ DB_INDEXER *indexer = NULL;
+ r = env->create_indexer(env, indexer_txn, &indexer, indexer_arg->src_db, indexer_arg->n_dest_db, indexer_arg->dest_db, NULL, 0); assert_zero(r);
+
+ r = indexer->build(indexer); assert_zero(r);
+
+ r = indexer->close(indexer); assert_zero(r);
+
+ r = indexer_txn->commit(indexer_txn, 0); assert_zero(r);
+
+ return arg;
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+
+ DBT key, val;
+ r = cursor->c_get(cursor, dbt_init(&key, 0, 0), dbt_init(&val, 0, 0), DB_NEXT);
+ assert(r == DB_NOTFOUND);
+
+ r = cursor->c_close(cursor); assert_zero(r);
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(void) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); assert_zero(r);
+ r = src_db->open(src_db, NULL, "0.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *dest_db = NULL;
+ r = db_create(&dest_db, env, 0); assert_zero(r);
+ r = dest_db->open(dest_db, NULL, "1.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // insert some
+ for (int i = 0; i < 246723; i++) {
+ int k = htonl(i);
+ int v = i;
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v, sizeof v);
+ r = src_db->put(src_db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ // run the indexer
+ struct indexer_arg indexer_arg = { env, src_db, 1, &dest_db };
+ toku_pthread_t pid;
+ r = toku_pthread_create(&pid, NULL, indexer_thread, &indexer_arg); assert_zero(r);
+
+ r = txn->abort(txn); assert_zero(r);
+
+ void *ret;
+ r = toku_pthread_join(pid, &ret); assert_zero(r);
+
+ verify_empty(env, src_db);
+ verify_empty(env, dest_db);
+
+ r = src_db->close(src_db, 0); assert_zero(r);
+
+ r = dest_db->close(dest_db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test();
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-put-commit.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-put-commit.cc
new file mode 100644
index 00000000000..59b40037125
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-put-commit.cc
@@ -0,0 +1,214 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "ydb.h"
+#include "toku_pthread.h"
+
+// this test reproduces the rollback log corruption that occurs when hot indexing runs concurrent with a long commit.
+// the concurrent operation occurs when the commit periodically releases the ydb lock which allows the hot indexer
+// to run. the hot indexer erroneously append to the rollback log that is in the process of being committed.
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+
+ lazy_assert(src_db != NULL && dest_db != NULL);
+
+ if (dest_key->flags == DB_DBT_REALLOC) {
+ dest_key->data = toku_realloc(dest_key->data, src_key->size);
+ memcpy(dest_key->data, src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ }
+ if (dest_val->flags == DB_DBT_REALLOC) {
+ dest_val->data = toku_realloc(dest_val->data, src_val->size);
+ memcpy(dest_val->data, src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ }
+
+ return 0;
+}
+
+struct indexer_arg {
+ DB_ENV *env;
+ DB *src_db;
+ int n_dest_db;
+ DB **dest_db;
+};
+
+static void *
+indexer_thread(void *arg) {
+ struct indexer_arg *indexer_arg = (struct indexer_arg *) arg;
+ DB_ENV *env = indexer_arg->env;
+ int r;
+
+ DB_TXN *indexer_txn = NULL;
+ r = env->txn_begin(env, NULL, &indexer_txn, 0); assert_zero(r);
+
+ DB_INDEXER *indexer = NULL;
+ r = env->create_indexer(env, indexer_txn, &indexer, indexer_arg->src_db, indexer_arg->n_dest_db, indexer_arg->dest_db, NULL, 0); assert_zero(r);
+
+ if (verbose) fprintf(stderr, "build start\n");
+ r = indexer->build(indexer); assert_zero(r);
+ if (verbose) fprintf(stderr, "build end\n");
+
+ r = indexer->close(indexer); assert_zero(r);
+
+ r = indexer_txn->commit(indexer_txn, 0); assert_zero(r);
+
+ return arg;
+}
+
+static void
+verify_full(DB_ENV *env, DB *db, int n) {
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+
+ int i = 0;
+ DBT key; dbt_init_realloc(&key);
+ DBT val; dbt_init_realloc(&val);
+ while (1) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r == DB_NOTFOUND)
+ break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == (int) htonl(i));
+ int v;
+ assert(val.size == sizeof v);
+ memcpy(&v, val.data, val.size);
+ assert(v == i);
+ i++;
+ }
+ assert(i == n);
+ toku_free(key.data);
+ toku_free(val.data);
+
+ r = cursor->c_close(cursor); assert_zero(r);
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(void) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); assert_zero(r);
+ r = src_db->open(src_db, NULL, "0.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *dest_db = NULL;
+ r = db_create(&dest_db, env, 0); assert_zero(r);
+ r = dest_db->open(dest_db, NULL, "1.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // insert some
+ int n = 246723;
+ for (int i = 0; i < n; i++) {
+ int k = htonl(i);
+ int v = i;
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v, sizeof v);
+ r = src_db->put(src_db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ // run the indexer
+ struct indexer_arg indexer_arg = { env, src_db, 1, &dest_db };
+ toku_pthread_t pid;
+ r = toku_pthread_create(&pid, NULL, indexer_thread, &indexer_arg); assert_zero(r);
+
+ if (verbose) fprintf(stderr, "commit start\n");
+ r = txn->commit(txn, 0); assert_zero(r);
+ if (verbose) fprintf(stderr, "commit end\n");
+
+ void *ret;
+ r = toku_pthread_join(pid, &ret); assert_zero(r);
+
+ verify_full(env, src_db, n);
+ verify_full(env, dest_db, n);
+
+ r = src_db->close(src_db, 0); assert_zero(r);
+
+ r = dest_db->close(dest_db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test();
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-put-multiple.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-put-multiple.cc
new file mode 100644
index 00000000000..d26a78b9d01
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-put-multiple.cc
@@ -0,0 +1,225 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+enum {NUM_DBS=1};
+enum {NUM_KV_PAIRS=3};
+struct kv_pair {
+ int64_t key;
+ int64_t val;
+};
+struct kv_pair kv_pairs[NUM_KV_PAIRS] = {{1,4},
+ {2,5},
+ {3,6}};
+
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+
+ (void) src_db;
+
+ uint32_t which = (uint32_t) (intptr_t) dest_db->app_private;
+
+ if (which == NUM_DBS) {
+ // primary
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+ } else {
+ // secondaries: switch the key and val
+ dbt_init(dest_key, src_val->data, src_val->size);
+ dbt_init(dest_val, src_key->data, src_key->size);
+ }
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+static int poll_print(void *extra, float progress) {
+ (void) progress;
+ (void) extra;
+ if ( verbose ) printf("poll_print %f\n", progress);
+ return 0;
+}
+
+const char *src_name="src.db";
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); CKERR(r);
+ r = src_db->open(src_db, NULL, src_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ src_db->app_private = (void *) NUM_DBS;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ DBT key, val;
+ dbt_init(&key, &kv_pairs[i].key, sizeof(kv_pairs[i].key));
+ dbt_init(&val, &kv_pairs[i].val, sizeof(kv_pairs[i].val));
+ r = src_db->put(src_db, txn, &key, &val, 0); CKERR(r);
+ }
+
+ DB *dbs[NUM_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ dbs[i]->app_private = (void *) (intptr_t) i;
+ }
+
+ DB_TXN *hottxn;
+ r = env->txn_begin(env, NULL, &hottxn, 0);
+ CKERR(r);
+
+ DB_INDEXER *indexer;
+ r = env->create_indexer(env, hottxn, &indexer, src_db, NUM_DBS, dbs, NULL, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ // setup putm
+ DB *putm_dbs[NUM_DBS+1];
+ for (int i = 0; i < NUM_DBS; i++)
+ putm_dbs[i] = dbs[i];
+ putm_dbs[NUM_DBS] = src_db;
+
+ DBT putm_keys[NUM_DBS+1], putm_vals[NUM_DBS+1];
+
+ uint32_t putm_flags[NUM_DBS+1];
+ for (int i = 0; i < NUM_DBS+1; i++)
+ putm_flags[i] = 0;
+
+ DBT prikey; int64_t pk;
+ dbt_init(&prikey, &pk, sizeof pk);
+
+ DBT prival; int64_t pv;
+ dbt_init(&prival, &pv, sizeof pv);
+
+ // putm (8,9)
+ pk = 8; pv = 9;
+ r = env_put_multiple_test_no_array(env, src_db, txn, &prikey, &prival, NUM_DBS+1, putm_dbs, putm_keys, putm_vals, putm_flags);
+ CKERR(r);
+
+ r = indexer->build(indexer);
+ CKERR(r);
+
+ // putm (9, 10)
+ pk = 9; pv = 10;
+ r = env_put_multiple_test_no_array(env, src_db, txn, &prikey, &prival, NUM_DBS+1, putm_dbs, putm_keys, putm_vals, putm_flags);
+ CKERR(r);
+
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = hottxn->commit(hottxn, DB_TXN_SYNC);
+ CKERR(r);
+
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = src_db->close(src_db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s\n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-simple-abort-put.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-simple-abort-put.cc
new file mode 100644
index 00000000000..59ba79df6f6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-simple-abort-put.cc
@@ -0,0 +1,130 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+ lazy_assert(src_db != NULL && dest_db != NULL);
+
+ toku_free(dest_key->data);
+ dest_key->data = toku_xmemdup(src_val->data, src_val->size);
+ dest_key->ulen = dest_key->size = src_val->size;
+ dest_val->size = 0;
+
+ return 0;
+}
+
+static void
+run_test(void) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); assert_zero(r);
+ r = src_db->open(src_db, NULL, "0.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *dest_db = NULL;
+ r = db_create(&dest_db, env, 0); assert_zero(r);
+ r = dest_db->open(dest_db, NULL, "1.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_TXN* index_txn = NULL;
+ r = env->txn_begin(env, NULL, &index_txn , 0); assert_zero(r);
+ DB_TXN* put_txn = NULL;
+ r = env->txn_begin(env, NULL, &put_txn , 0); assert_zero(r);
+
+ DBT key,data;
+ r = src_db->put(
+ src_db,
+ put_txn,
+ dbt_init(&key, "hello", 6),
+ dbt_init(&data, "there", 6),
+ 0
+ );
+
+ DB_INDEXER *indexer = NULL;
+ r = env->create_indexer(env, index_txn, &indexer, src_db, 1, &dest_db, NULL, 0); assert_zero(r);
+ r = indexer->build(indexer); assert_zero(r);
+ r = indexer->close(indexer); assert_zero(r);
+ r = index_txn->abort(index_txn); assert_zero(r);
+
+ r = put_txn->abort(put_txn); assert_zero(r);
+
+
+ r = src_db->close(src_db, 0); assert_zero(r);
+ r = dest_db->close(dest_db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test();
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-simple-abort.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-simple-abort.cc
new file mode 100644
index 00000000000..c7fe44ff509
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-simple-abort.cc
@@ -0,0 +1,118 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+
+ lazy_assert(src_db != NULL && dest_db != NULL);
+
+ dest_key->data = src_val->data;
+ dest_key->size = src_val->size;
+ dest_val->size = 0;
+
+ return 0;
+}
+
+static void
+run_test(void) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); assert_zero(r);
+ r = src_db->open(src_db, NULL, "0.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *dest_db = NULL;
+ r = db_create(&dest_db, env, 0); assert_zero(r);
+ r = dest_db->open(dest_db, NULL, "1.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DB_INDEXER *indexer = NULL;
+ r = env->create_indexer(env, txn, &indexer, src_db, 1, &dest_db, NULL, 0); assert_zero(r);
+
+ r = indexer->abort(indexer); assert_zero(r);
+
+ r = txn->abort(txn); assert_zero(r);
+
+ r = src_db->close(src_db, 0); assert_zero(r);
+ r = dest_db->close(dest_db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test();
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-test.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-test.cc
new file mode 100644
index 00000000000..e688a4570d5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-test.cc
@@ -0,0 +1,596 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test the hotindexer undo do function
+// read a description of the live transactions and a leafentry from a test file, run the undo do function,
+// and print out the actions taken by the undo do function while processing the leafentry
+
+#include "test.h"
+
+#include <ft/ule.h>
+#include <ft/ule-internal.h>
+#include <ft/le-cursor.h>
+#include <ft/txn/xids.h>
+
+#include "indexer-internal.h"
+
+struct txn {
+ TXNID xid;
+ TOKUTXN_STATE state;
+};
+
+struct live {
+ int n;
+ int o;
+ struct txn *txns;
+};
+
+static void
+live_init(struct live *live) {
+ live->n = live->o = 0;
+ live->txns = NULL;
+}
+
+static void
+live_destroy(struct live *live) {
+ toku_free(live->txns);
+}
+
+static void
+live_add(struct live *live, TXNID xid, TOKUTXN_STATE state) {
+ if (live->o >= live->n) {
+ int newn = live->n == 0 ? 1 : live->n * 2;
+ live->txns = (struct txn *) toku_realloc(live->txns, newn * sizeof (struct txn));
+ resource_assert(live->txns);
+ live->n = newn;
+ }
+ live->txns[live->o++] = (struct txn ) { xid, state };
+}
+
+static TOKUTXN_STATE
+lookup_txn_state(struct live *live, TXNID xid) {
+ TOKUTXN_STATE r = TOKUTXN_RETIRED;
+ for (int i = 0; i < live->o; i++) {
+ if (live->txns[i].xid == xid) {
+ r = live->txns[i].state;
+ break;
+ }
+ }
+ return r;
+}
+
+// live transaction ID set
+struct live live_xids;
+
+static void
+uxr_init(UXR uxr, uint8_t type, void *val, uint32_t vallen, TXNID xid) {
+ uxr->type = type;
+ uxr->valp = toku_malloc(vallen); resource_assert(uxr->valp);
+ memcpy(uxr->valp, val, vallen);
+ uxr->vallen = vallen;
+ uxr->xid = xid;
+}
+
+static void
+uxr_destroy(UXR uxr) {
+ toku_free(uxr->valp);
+ uxr->valp = NULL;
+}
+
+static ULE
+ule_init(ULE ule) {
+ ule->num_puxrs = 0;
+ ule->num_cuxrs = 0;
+ ule->uxrs = ule->uxrs_static;
+ return ule;
+}
+
+static void
+ule_destroy(ULE ule) {
+ for (unsigned int i = 0; i < ule->num_cuxrs + ule->num_puxrs; i++) {
+ uxr_destroy(&ule->uxrs[i]);
+ }
+}
+
+static void
+ule_add_provisional(ULE ule, UXR uxr) {
+ invariant(ule->num_cuxrs + ule->num_puxrs + 1 <= MAX_TRANSACTION_RECORDS*2);
+ ule->uxrs[ule->num_cuxrs + ule->num_puxrs] = *uxr;
+ ule->num_puxrs++;
+}
+
+static void
+ule_add_committed(ULE ule, UXR uxr) {
+ lazy_assert(ule->num_puxrs == 0);
+ invariant(ule->num_cuxrs + 1 <= MAX_TRANSACTION_RECORDS*2);
+ ule->uxrs[ule->num_cuxrs] = *uxr;
+ ule->num_cuxrs++;
+}
+
+static ULE
+ule_create(void) {
+ ULE ule = (ULE) toku_calloc(1, sizeof (ULE_S)); resource_assert(ule);
+ if (ule)
+ ule_init(ule);
+ return ule;
+}
+
+static void
+ule_free(ULE ule) {
+ ule_destroy(ule);
+ toku_free(ule);
+}
+
+static void
+print_xids(XIDS xids) {
+ printf("[");
+ if (xids->num_xids == 0)
+ printf("0");
+ else {
+ for (int i = 0; i < xids->num_xids; i++) {
+ printf("%" PRIu64, xids->ids[i]);
+ if (i+1 < xids->num_xids)
+ printf(",");
+ }
+ }
+ printf("] ");
+}
+
+static void
+print_dbt(DBT *dbt) {
+ printf("%.*s ", dbt->size, (char *) dbt->data);
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+
+ lazy_assert(src_db != NULL && dest_db != NULL);
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->data = src_val->data;
+ dest_key->size = src_val->size;
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->data = toku_realloc(dest_key->data, src_val->size);
+ memcpy(dest_key->data, src_val->data, src_val->size);
+ dest_key->size = src_val->size;
+ break;
+ default:
+ lazy_assert(0);
+ }
+
+ if (dest_val)
+ switch (dest_val->flags) {
+ case 0:
+ lazy_assert(0);
+ break;
+ case DB_DBT_REALLOC:
+ dest_val->data = toku_realloc(dest_val->data, src_key->size);
+ memcpy(dest_val->data, src_key->data, src_key->size);
+ dest_val->size = src_key->size;
+ break;
+ default:
+ lazy_assert(0);
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ toku_dbt_array_resize(dest_keys, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_key; (void) src_key; (void) src_data;
+
+ lazy_assert(src_db != NULL && dest_db != NULL);
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->data = src_data->data;
+ dest_key->size = src_data->size;
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->data = toku_realloc(dest_key->data, src_data->size);
+ memcpy(dest_key->data, src_data->data, src_data->size);
+ dest_key->size = src_data->size;
+ break;
+ default:
+ lazy_assert(0);
+ }
+ return 0;
+}
+
+
+static DB_INDEXER *test_indexer = NULL;
+static DB *test_hotdb = NULL;
+
+static TOKUTXN_STATE
+test_xid_state(DB_INDEXER *indexer, TXNID xid) {
+ invariant(indexer == test_indexer);
+ TOKUTXN_STATE r = lookup_txn_state(&live_xids, xid);
+ return r;
+}
+
+static void
+test_lock_key(DB_INDEXER *indexer, TXNID xid, DB *hotdb, DBT *key) {
+ invariant(indexer == test_indexer);
+ invariant(hotdb == test_hotdb);
+ TOKUTXN_STATE txn_state = test_xid_state(indexer, xid);
+ invariant(txn_state == TOKUTXN_LIVE || txn_state == TOKUTXN_PREPARING);
+ printf("lock [%" PRIu64 "] ", xid);
+ print_dbt(key);
+ printf("\n");
+}
+
+static int
+test_delete_provisional(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids) {
+ invariant(indexer == test_indexer);
+ invariant(hotdb == test_hotdb);
+ printf("delete_provisional ");
+ print_xids(xids);
+ print_dbt(hotkey);
+ printf("\n");
+ return 0;
+}
+
+static int
+test_delete_committed(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids) {
+ invariant(indexer == test_indexer);
+ invariant(hotdb == test_hotdb);
+ printf("delete_committed ");
+ print_xids(xids);
+ print_dbt(hotkey);
+ printf("\n");
+ return 0;
+}
+
+static int
+test_insert_provisional(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, DBT *hotval, XIDS xids) {
+ invariant(indexer == test_indexer);
+ invariant(hotdb == test_hotdb);
+ printf("insert_provisional ");
+ print_xids(xids);
+ print_dbt(hotkey);
+ print_dbt(hotval);
+ printf("\n");
+ return 0;
+}
+
+static int
+test_insert_committed(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, DBT *hotval, XIDS xids) {
+ invariant(indexer == test_indexer);
+ invariant(hotdb == test_hotdb);
+ printf("insert_committed ");
+ print_xids(xids);
+ print_dbt(hotkey);
+ print_dbt(hotval);
+ printf("\n");
+ return 0;
+}
+
+static int
+test_commit_any(DB_INDEXER *indexer, DB *hotdb, DBT *hotkey, XIDS xids) {
+ invariant(indexer == test_indexer);
+ invariant(hotdb == test_hotdb);
+ printf("commit_any ");
+ print_xids(xids);
+ print_dbt(hotkey);
+ printf("\n");
+ return 0;
+}
+
+static int
+split_fields(char *line, char *fields[], int maxfields) {
+ int i;
+ for (i = 0; i < maxfields; i++, line = NULL) {
+ fields[i] = strtok(line, " ");
+ if (fields[i] == NULL)
+ break;
+ }
+ return i;
+}
+
+static int
+read_line(char **line_ptr, size_t *len_ptr, FILE *f) {
+ char *line = *line_ptr;
+ size_t len = 0;
+ bool in_comment = false;
+ while (1) {
+ int c = fgetc(f);
+ if (c == EOF)
+ break;
+ else if (c == '\n') {
+ in_comment = false;
+ if (len > 0)
+ break;
+ } else {
+ if (c == '#')
+ in_comment = true;
+ if (!in_comment) {
+ XREALLOC_N(len+1, line);
+ line[len++] = c;
+ }
+ }
+ }
+ if (len > 0) {
+ XREALLOC_N(len+1, line);
+ line[len] = '\0';
+ }
+ *line_ptr = line;
+ *len_ptr = len;
+ return len == 0 ? -1 : 0;
+}
+
+struct saved_lines_t {
+ char** savedlines;
+ uint32_t capacity;
+ uint32_t used;
+};
+
+static void
+save_line(char** line, saved_lines_t* saved) {
+ if (saved->capacity == saved->used) {
+ if (saved->capacity == 0) {
+ saved->capacity = 1;
+ }
+ saved->capacity *= 2;
+ XREALLOC_N(saved->capacity, saved->savedlines);
+ }
+ saved->savedlines[saved->used++] = *line;
+ *line = nullptr;
+}
+
+static int
+read_test(char *testname, ULE ule, DBT* key, saved_lines_t* saved) {
+ int r = 0;
+ FILE *f = fopen(testname, "r");
+ if (f) {
+ char *line = NULL;
+ size_t len = 0;
+ while (read_line(&line, &len, f) != -1) {
+ // printf("%s", line);
+
+ const int maxfields = 8;
+ char *fields[maxfields];
+ int nfields = split_fields(line, fields, maxfields);
+ // for (int i = 0; i < nfields; i++); printf("%s ", fields[i]); printf("\n");
+
+ if (nfields < 1)
+ continue;
+ // live xid...
+ if (strcmp(fields[0], "live") == 0) {
+ for (int i = 1; i < nfields; i++)
+ live_add(&live_xids, atoll(fields[i]), TOKUTXN_LIVE);
+ continue;
+ }
+ // xid <XID> [live|committing|aborting]
+ if (strcmp(fields[0], "xid") == 0 && nfields == 3) {
+ TXNID xid = atoll(fields[1]);
+ TOKUTXN_STATE state = TOKUTXN_RETIRED;
+ if (strcmp(fields[2], "live") == 0)
+ state = TOKUTXN_LIVE;
+ else if (strcmp(fields[2], "preparing") == 0)
+ state = TOKUTXN_PREPARING;
+ else if (strcmp(fields[2], "committing") == 0)
+ state = TOKUTXN_COMMITTING;
+ else if (strcmp(fields[2], "aborting") == 0)
+ state = TOKUTXN_ABORTING;
+ else
+ assert(0);
+ live_add(&live_xids, xid, state);
+ continue;
+ }
+ // key KEY
+ if (strcmp(fields[0], "key") == 0 && nfields == 2) {
+ save_line(&line, saved);
+ dbt_init(key, fields[1], strlen(fields[1]));
+ continue;
+ }
+ // insert committed|provisional XID DATA
+ if (strcmp(fields[0], "insert") == 0 && nfields == 4) {
+ save_line(&line, saved);
+ UXR_S uxr_s;
+ uxr_init(&uxr_s, XR_INSERT, fields[3], strlen(fields[3]), atoll(fields[2]));
+ if (fields[1][0] == 'p')
+ ule_add_provisional(ule, &uxr_s);
+ if (fields[1][0] == 'c')
+ ule_add_committed(ule, &uxr_s);
+ continue;
+ }
+ // delete committed|provisional XID
+ if (strcmp(fields[0], "delete") == 0 && nfields == 3) {
+ UXR_S uxr_s;
+ uxr_init(&uxr_s, XR_DELETE, NULL, 0, atoll(fields[2]));
+ if (fields[1][0] == 'p')
+ ule_add_provisional(ule, &uxr_s);
+ if (fields[1][0] == 'c')
+ ule_add_committed(ule, &uxr_s);
+ continue;
+ }
+ // placeholder XID
+ if (strcmp(fields[0], "placeholder") == 0 && nfields == 2) {
+ UXR_S uxr_s;
+ uxr_init(&uxr_s, XR_PLACEHOLDER, NULL, 0, atoll(fields[1]));
+ ule_add_provisional(ule, &uxr_s);
+ continue;
+ }
+ // placeholder provisional XID
+ if (strcmp(fields[0], "placeholder") == 0 && nfields == 3 && fields[1][0] == 'p') {
+ UXR_S uxr_s;
+ uxr_init(&uxr_s, XR_PLACEHOLDER, NULL, 0, atoll(fields[2]));
+ ule_add_provisional(ule, &uxr_s);
+ continue;
+ }
+ fprintf(stderr, "%s???\n", line);
+ r = EINVAL;
+ }
+ toku_free(line);
+ fclose(f);
+ } else {
+ r = errno;
+ fprintf(stderr, "fopen %s errno=%d\n", testname, errno);
+ }
+ return r;
+ }
+
+static int
+run_test(char *envdir, char *testname) {
+ if (verbose)
+ printf("%s\n", testname);
+
+ live_init(&live_xids);
+
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_redzone(env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, envdir, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); assert_zero(r);
+ r = src_db->open(src_db, NULL, "0.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *dest_db = NULL;
+ r = db_create(&dest_db, env, 0); assert_zero(r);
+ r = dest_db->open(dest_db, NULL, "1.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DB_INDEXER *indexer = NULL;
+ r = env->create_indexer(env, txn, &indexer, src_db, 1, &dest_db, NULL, 0); assert_zero(r);
+
+ // set test callbacks
+ indexer->i->test_xid_state = test_xid_state;
+ indexer->i->test_lock_key = test_lock_key;
+ indexer->i->test_delete_provisional = test_delete_provisional;
+ indexer->i->test_delete_committed = test_delete_committed;
+ indexer->i->test_insert_provisional = test_insert_provisional;
+ indexer->i->test_insert_committed = test_insert_committed;
+ indexer->i->test_commit_any = test_commit_any;
+
+ // verify indexer and hotdb in the callbacks
+ test_indexer = indexer;
+ test_hotdb = dest_db;
+
+ // create a ule
+ ULE ule = ule_create();
+ ule_init(ule);
+
+ saved_lines_t saved;
+ ZERO_STRUCT(saved);
+ // read the test
+ DBT key;
+ ZERO_STRUCT(key);
+ r = read_test(testname, ule, &key, &saved);
+ if (r != 0)
+ return r;
+
+ r = indexer->i->undo_do(indexer, dest_db, &key, ule); assert_zero(r);
+
+ ule_free(ule);
+ key.data = NULL;
+
+ for (uint32_t i = 0; i < saved.used; i++) {
+ toku_free(saved.savedlines[i]);
+ }
+ toku_free(saved.savedlines);
+
+ r = indexer->close(indexer); assert_zero(r);
+
+ r = txn->abort(txn); assert_zero(r);
+
+ r = src_db->close(src_db, 0); assert_zero(r);
+ r = dest_db->close(dest_db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+
+ live_destroy(&live_xids);
+
+ return r;
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+
+ // parse_args(argc, argv);
+ int i;
+ for (i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+
+ break;
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert_zero(r);
+ for (r = 0 ; r == 0 && i < argc; i++) {
+ char *testname = argv[i];
+ char pid[10];
+ sprintf(pid, "%d", toku_os_getpid());
+ char envdir[TOKU_PATH_MAX+1];
+ toku_path_join(envdir, 2, TOKU_TEST_FILENAME, pid);
+
+ toku_os_recursive_delete(envdir);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ r = run_test(envdir, testname);
+ }
+
+ return r;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/README b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/README
new file mode 100644
index 00000000000..184669b99e2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/README
@@ -0,0 +1,77 @@
+= Hotindexer undo do testing =
+
+The hotindexer undo do function is tested by feeding it a leafentry,
+capturing the actions taken on the hot dictionary,
+and comparing the actions with the expected actions.
+The test passes if the actions match the expected actions.
+
+Each test is described by a .test file and a .result file.
+The .test file describes the set of transactions that are live
+when the undo do function is called as well as the transaction records
+that comprise a leaf entry.
+
+The .result file describes the actions taken by the undo do function
+when processing the leaf entry.
+
+= Contents of a *.test =
+
+Comments begin with '#'.
+
+An <XIDLIST> is a list of transaction id's separated by a SPACE.
+
+<XIDS> is a stack of transaction id's separated by a COMMA.
+
+An <XID> is a 64 bit number.
+
+A <KEY> is a string.
+
+A <VALUE> is a string.
+
+The field separator is a single SPACE.
+
+== set the leaf entry key ==
+key <KEY>
+
+== add transaction IDs to the live transaction set ==
+live <XIDLIST>
+
+the live transaction set is initially empty
+
+xid <XID> [live|preparing|committing|aborting]
+
+== push a delete transaction record onto the leaf entry stack ==
+delete [committed|provisional] <XID>
+
+== push an insert transaction records onto the leaf entry stack ==
+insert [committed|provisional] <XID> <VALUE>
+
+== push a placeholder onto the leaf entry stack ==
+placeholder <XID>
+
+= Contents of a *.result =
+
+== insert committed ==
+insert_committed [<XIDS>] <KEY> <VALUE>
+commit_any [<XIDS>] <KEY>
+
+== delete committed ==
+delete_committed [<XIDS>] <KEY>
+commit_any [<XIDS>] <KEY>
+
+== insert provisional ==
+insert_provisional [<XIDS>] <KEY> <VALUE>
+
+== delete provisional ==
+delete_provisional [<XIDS>] <KEY>
+
+== lock ==
+lock [<XIDS>] <KEY>
+
+= Schema =
+
+Source dictionary: <source key, source data>
+
+Hot dictionary: <source data, source key>
+
+
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.d200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.d200.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.d200.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.d200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.d200.test
new file mode 100644
index 00000000000..77f2fbbbacf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.d200.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+delete committed 100
+delete committed 200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.i200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.i200.result
new file mode 100644
index 00000000000..2c56b6d17de
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.i200.result
@@ -0,0 +1,2 @@
+insert_committed [200] v100 k1
+commit_any [200] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.i200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.i200.test
new file mode 100644
index 00000000000..e58ffbb6a67
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.i200.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+delete committed 100
+insert committed 200 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.test
new file mode 100644
index 00000000000..f7099778036
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.d100.test
@@ -0,0 +1,3 @@
+key k1
+delete committed 0
+delete committed 100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.d200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.d200.result
new file mode 100644
index 00000000000..be42bdfb6e7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.d200.result
@@ -0,0 +1,4 @@
+insert_committed [100] v100 k1
+commit_any [100] v100
+delete_committed [200] v100
+commit_any [200] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.d200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.d200.test
new file mode 100644
index 00000000000..a2d7a805ac6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.d200.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+insert committed 100 v100
+delete committed 200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.i200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.i200.result
new file mode 100644
index 00000000000..a7fe43c0288
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.i200.result
@@ -0,0 +1,6 @@
+insert_committed [100] v100 k1
+commit_any [100] v100
+delete_committed [200] v100
+insert_committed [200] v200 k1
+commit_any [200] v100
+commit_any [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.i200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.i200.test
new file mode 100644
index 00000000000..cd4ed356de7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.i200.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+insert committed 100 v100
+insert committed 200 v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.result
new file mode 100644
index 00000000000..2dd40c838b0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.result
@@ -0,0 +1,2 @@
+insert_committed [100] v100 k1
+commit_any [100] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.test
new file mode 100644
index 00000000000..ef6956884ae
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.i100.test
@@ -0,0 +1,3 @@
+key k1
+delete committed 0
+insert committed 100 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.test
new file mode 100644
index 00000000000..ebf9be61f6b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.d0.test
@@ -0,0 +1,3 @@
+# this test runs the undo do function on a leaf entry that consists of a single committed delete
+key k1
+delete committed 0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.d200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.d200.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.d200.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.d200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.d200.test
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.d200.test
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.i200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.i200.result
new file mode 100644
index 00000000000..e63e2f5df4a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.i200.result
@@ -0,0 +1,5 @@
+insert_committed [0] v0 k1
+delete_committed [100] v0
+commit_any [100] v0
+insert_committed [200] v100 k1
+commit_any [200] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.i200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.i200.test
new file mode 100644
index 00000000000..65a7f0e4b9c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.i200.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+delete committed 100
+insert committed 200 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.result
new file mode 100644
index 00000000000..8523a779020
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.result
@@ -0,0 +1,3 @@
+insert_committed [0] v10 k1
+delete_committed [100] v10
+commit_any [100] v10
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.test
new file mode 100644
index 00000000000..d6a1e6b8c91
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.d100.test
@@ -0,0 +1,3 @@
+key k1
+insert committed 0 v10
+delete committed 100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.d200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.d200.result
new file mode 100644
index 00000000000..f866683bb89
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.d200.result
@@ -0,0 +1,7 @@
+insert_committed [0] v0 k1
+delete_committed [100] v0
+insert_committed [100] v100 k1
+commit_any [100] v0
+commit_any [100] v100
+delete_committed [200] v100
+commit_any [200] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.d200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.d200.test
new file mode 100644
index 00000000000..f9902c0937c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.d200.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+insert committed 100 v100
+delete committed 200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.i200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.i200.result
new file mode 100644
index 00000000000..1d7c7f12b12
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.i200.result
@@ -0,0 +1,9 @@
+insert_committed [0] v0 k1
+delete_committed [100] v0
+insert_committed [100] v100 k1
+commit_any [100] v0
+commit_any [100] v100
+delete_committed [200] v100
+insert_committed [200] v200 k1
+commit_any [200] v100
+commit_any [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.i200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.i200.test
new file mode 100644
index 00000000000..d3b901e65d5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.i200.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+insert committed 100 v100
+insert committed 200 v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.result
new file mode 100644
index 00000000000..425bf7e10d5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.result
@@ -0,0 +1,5 @@
+insert_committed [0] v10 k1
+delete_committed [100] v10
+insert_committed [100] v20 k1
+commit_any [100] v10
+commit_any [100] v20
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.test
new file mode 100644
index 00000000000..6c868efa398
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.i100.test
@@ -0,0 +1,3 @@
+key k1
+insert committed 0 v10
+insert committed 100 v20
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.result
new file mode 100644
index 00000000000..88d7c9f382a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.result
@@ -0,0 +1 @@
+insert_committed [0] v100 k1
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.test
new file mode 100644
index 00000000000..20df13923e6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/commit.i0.test
@@ -0,0 +1,3 @@
+# commited insert
+key k1
+insert committed 0 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/insert.300.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/insert.300.result
new file mode 100644
index 00000000000..5b73e538efe
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/insert.300.result
@@ -0,0 +1,9 @@
+insert_committed [0] v100 k1
+delete_committed [200] v100
+insert_committed [200] v200 k1
+commit_any [200] v100
+commit_any [200] v200
+delete_committed [300] v200
+insert_committed [300] v300 k1
+commit_any [300] v200
+commit_any [300] v300
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/insert.300.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/insert.300.test
new file mode 100644
index 00000000000..915c3781a11
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/insert.300.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v100
+insert committed 200 v200
+insert committed 300 v300
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.live.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.live.result
new file mode 100644
index 00000000000..2b7db461df1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.live.result
@@ -0,0 +1,2 @@
+insert_provisional [300,301,302] v10 k1
+lock [300] v10
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.live.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.live.test
new file mode 100644
index 00000000000..82b2e311217
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.live.test
@@ -0,0 +1,7 @@
+live 300 301 302
+key k1
+delete committed 0
+placeholder 300
+placeholder 301
+insert provisional 302 v10
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.result
new file mode 100644
index 00000000000..292b9ca01c1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.result
@@ -0,0 +1 @@
+insert_committed [300] v10 k1
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.test
new file mode 100644
index 00000000000..b65532b60bd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.0.test
@@ -0,0 +1,6 @@
+key k1
+delete committed 0
+placeholder 300
+placeholder 301
+insert provisional 302 v10
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.1.live.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.1.live.result
new file mode 100644
index 00000000000..d2f7b01bc8d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.1.live.result
@@ -0,0 +1,2 @@
+insert_provisional [300,301] v10 k1
+lock [300] v10
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.1.live.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.1.live.test
new file mode 100644
index 00000000000..e6dc15fda8d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.1.live.test
@@ -0,0 +1,7 @@
+live 300 301
+key k1
+delete committed 0
+placeholder 300
+placeholder 301
+insert provisional 302 v10
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.live.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.live.result
new file mode 100644
index 00000000000..527a0db8e99
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.live.result
@@ -0,0 +1,6 @@
+insert_committed [100] v10 k1
+commit_any [100] v10
+delete_provisional [300,301,302] v10
+lock [300] v10
+insert_provisional [300,301,302] v20 k1
+lock [300] v20
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.live.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.live.test
new file mode 100644
index 00000000000..da6e8834c9f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.live.test
@@ -0,0 +1,8 @@
+live 300 301 302
+key k1
+delete committed 0
+insert committed 100 v10
+placeholder 300
+placeholder 301
+insert provisional 302 v20
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.result
new file mode 100644
index 00000000000..772eb58576d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.result
@@ -0,0 +1,5 @@
+insert_committed [100] v10 k1
+commit_any [100] v10
+delete_committed [300] v10
+insert_committed [300] v20 k1
+commit_any [300] v10
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.test
new file mode 100644
index 00000000000..6dc9f80bcab
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.2.test
@@ -0,0 +1,7 @@
+key k1
+delete committed 0
+insert committed 100 v10
+placeholder 300
+placeholder 301
+insert provisional 302 v20
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.3.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.3.result
new file mode 100644
index 00000000000..a3b11d6f41e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.3.result
@@ -0,0 +1,4 @@
+insert_committed [300] v18 k1
+delete_committed [300] v18
+insert_committed [300] v20 k1
+commit_any [300] v18
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.3.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.3.test
new file mode 100644
index 00000000000..29c8871a2f6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/placeholder.3.test
@@ -0,0 +1,5 @@
+key k1
+delete committed 0
+insert provisional 300 v18
+placeholder 301
+insert provisional 302 v20
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov-2.py b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov-2.py
new file mode 100644
index 00000000000..a61174aa8ea
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov-2.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+# generate hotindexing undo provisional tests with 2 nested transactions
+
+import sys
+
+def print_tr(fp, tr, trstack):
+ trtype = tr[0]
+ xid = tr[1:]
+ if trtype == 'i':
+ print >>fp, "insert", trstack, xid, "v"+xid
+ if trtype == 'd':
+ print >>fp, "delete", trstack, xid
+ if trtype == 'p':
+ print >>fp, "placeholder", trstack, xid
+
+def print_test(fp, live, commit, prov0, prov1):
+ if live != "":
+ for xid in live.split(","):
+ print >>fp, "live", xid
+ print >>fp, "key k1"
+ print_tr(fp, commit, "committed")
+ print_tr(fp, prov0, "provisional")
+ print_tr(fp, prov1, "provisional")
+
+def main():
+ # live transactions
+ for live in ["", "200", "200,201"]:
+ # committed transaction records
+ for commit in ["i0", "d0"]:
+ # provisional level 0 transaction records
+ for prov0 in ["i200", "d200", "p200"]:
+ # provisional level 1 transaction records
+ for prov1 in ["i201", "d201"]:
+ if live == "":
+ fname = "prov.%s.%s.%s.test" % (commit, prov0, prov1)
+ else:
+ fname = "prov.live%s.%s.%s.%s.test" % (live, commit, prov0, prov1)
+ print fname
+ fp = open(fname, "w")
+ if fp:
+ print_test(fp, live, commit, prov0, prov1)
+ fp.close()
+ return 0
+
+sys.exit(main())
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.live.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.live.result
new file mode 100644
index 00000000000..53609227a56
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.live.result
@@ -0,0 +1,15 @@
+insert_committed [0] v10 k1
+delete_committed [100] v10
+insert_committed [100] v20 k1
+commit_any [100] v10
+commit_any [100] v20
+delete_committed [200] v20
+insert_committed [200] v10 k1
+commit_any [200] v20
+commit_any [200] v10
+delete_provisional [300,301] v10
+lock [300] v10
+insert_provisional [300,301] v30 k1
+lock [300] v30
+delete_provisional [300,301,302] v30
+lock [300] v30
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.live.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.live.test
new file mode 100644
index 00000000000..10056eedc5d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.live.test
@@ -0,0 +1,8 @@
+live 300 301 302
+key k1
+insert committed 0 v10
+insert committed 100 v20
+insert committed 200 v10
+placeholder 300
+insert provisional 301 v30
+delete provisional 302
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.result
new file mode 100644
index 00000000000..304c2c0dc55
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.result
@@ -0,0 +1,14 @@
+insert_committed [0] v10 k1
+delete_committed [100] v10
+insert_committed [100] v20 k1
+commit_any [100] v10
+commit_any [100] v20
+delete_committed [200] v20
+insert_committed [200] v10 k1
+commit_any [200] v20
+commit_any [200] v10
+delete_committed [300] v10
+insert_committed [300] v30 k1
+delete_committed [300] v30
+commit_any [300] v10
+commit_any [300] v30
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.test
new file mode 100644
index 00000000000..69043006a4e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.1.test
@@ -0,0 +1,7 @@
+key k1
+insert committed 0 v10
+insert committed 100 v20
+insert committed 200 v10
+placeholder 300
+insert provisional 301 v30
+delete provisional 302
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.d0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.d0.i100.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.d0.i100.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.d0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.d0.i100.test
new file mode 100644
index 00000000000..ee87b18095d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.d0.i100.test
@@ -0,0 +1,4 @@
+xid 100 aborting
+key k1
+delete committed 0
+insert provisional 100 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.i100.i200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.i100.i200.result
new file mode 100644
index 00000000000..88d7c9f382a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.i100.i200.result
@@ -0,0 +1 @@
+insert_committed [0] v100 k1
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.i100.i200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.i100.i200.test
new file mode 100644
index 00000000000..a6bd167cf5c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.aborting.i100.i200.test
@@ -0,0 +1,4 @@
+xid 100 aborting
+key k1
+insert committed 0 v100
+insert provisional 100 v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.d0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.d0.i100.result
new file mode 100644
index 00000000000..b32d3a59d63
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.d0.i100.result
@@ -0,0 +1 @@
+insert_committed [100] v100 k1
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.d0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.d0.i100.test
new file mode 100644
index 00000000000..9558c0e4b49
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.d0.i100.test
@@ -0,0 +1,4 @@
+xid 100 committing
+key k1
+delete committed 0
+insert provisional 100 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.i100.i200.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.i100.i200.result
new file mode 100644
index 00000000000..be161e5ce36
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.i100.i200.result
@@ -0,0 +1,4 @@
+insert_committed [0] v100 k1
+delete_committed [100] v100
+insert_committed [100] v200 k1
+commit_any [100] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.i100.i200.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.i100.i200.test
new file mode 100644
index 00000000000..c8db5bbaec7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.committing.i100.i200.test
@@ -0,0 +1,4 @@
+xid 100 committing
+key k1
+insert committed 0 v100
+insert provisional 100 v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d100.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d100.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d100.test
new file mode 100644
index 00000000000..37ab505c192
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d100.test
@@ -0,0 +1,3 @@
+key k1
+delete committed 0
+delete provisional 100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.d201.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.d201.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.d201.test
new file mode 100644
index 00000000000..960d9c5cd97
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.d201.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+delete provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.i201.result
new file mode 100644
index 00000000000..d05f2f64b39
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.i201.result
@@ -0,0 +1 @@
+insert_committed [200] v201 k1
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.i201.test
new file mode 100644
index 00000000000..c7fa5450df9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.d200.i201.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+delete provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i100.result
new file mode 100644
index 00000000000..b32d3a59d63
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i100.result
@@ -0,0 +1 @@
+insert_committed [100] v100 k1
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i100.test
new file mode 100644
index 00000000000..adcde7599ff
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i100.test
@@ -0,0 +1,3 @@
+key k1
+delete committed 0
+insert provisional 100 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.d201.result
new file mode 100644
index 00000000000..38bfcbbc5d2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.d201.result
@@ -0,0 +1,3 @@
+insert_committed [200] v200 k1
+delete_committed [200] v200
+commit_any [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.d201.test
new file mode 100644
index 00000000000..fa52fe19a19
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.d201.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+insert provisional 200 v200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.i201.result
new file mode 100644
index 00000000000..217272ddda2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.i201.result
@@ -0,0 +1,4 @@
+insert_committed [200] v200 k1
+delete_committed [200] v200
+insert_committed [200] v201 k1
+commit_any [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.i201.test
new file mode 100644
index 00000000000..568cdc171fe
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.i200.i201.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.d201.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.d201.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.d201.test
new file mode 100644
index 00000000000..cf56d8df47f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.d201.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+placeholder provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.i201.result
new file mode 100644
index 00000000000..d05f2f64b39
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.i201.result
@@ -0,0 +1 @@
+insert_committed [200] v201 k1
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.i201.test
new file mode 100644
index 00000000000..60c4b6c08ed
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.d0.p200.i201.test
@@ -0,0 +1,4 @@
+key k1
+delete committed 0
+placeholder provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d100.result
new file mode 100644
index 00000000000..8523a779020
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d100.result
@@ -0,0 +1,3 @@
+insert_committed [0] v10 k1
+delete_committed [100] v10
+commit_any [100] v10
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d100.test
new file mode 100644
index 00000000000..05d444cdccc
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d100.test
@@ -0,0 +1,3 @@
+key k1
+insert committed 0 v10
+delete provisional 100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.d201.result
new file mode 100644
index 00000000000..f22da09c1c9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.d201.result
@@ -0,0 +1,3 @@
+insert_committed [0] v0 k1
+delete_committed [200] v0
+commit_any [200] v0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.d201.test
new file mode 100644
index 00000000000..cd919eb4c87
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.d201.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+delete provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.i201.result
new file mode 100644
index 00000000000..75f2f8f18fb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.i201.result
@@ -0,0 +1,4 @@
+insert_committed [0] v0 k1
+delete_committed [200] v0
+insert_committed [200] v201 k1
+commit_any [200] v0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.i201.test
new file mode 100644
index 00000000000..7c5a696acac
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.d200.i201.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+delete provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i100.result
new file mode 100644
index 00000000000..6946d3add14
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i100.result
@@ -0,0 +1,4 @@
+insert_committed [0] v10 k1
+delete_committed [100] v10
+insert_committed [100] v100 k1
+commit_any [100] v10
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i100.test
new file mode 100644
index 00000000000..c29f1a642a4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i100.test
@@ -0,0 +1,3 @@
+key k1
+insert committed 0 v10
+insert provisional 100 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.d201.result
new file mode 100644
index 00000000000..d69c4c45fc2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.d201.result
@@ -0,0 +1,6 @@
+insert_committed [0] v0 k1
+delete_committed [200] v0
+insert_committed [200] v200 k1
+delete_committed [200] v200
+commit_any [200] v0
+commit_any [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.d201.test
new file mode 100644
index 00000000000..c20923b86c0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.d201.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.i201.result
new file mode 100644
index 00000000000..04f9a49ce12
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.i201.result
@@ -0,0 +1,7 @@
+insert_committed [0] v0 k1
+delete_committed [200] v0
+insert_committed [200] v200 k1
+delete_committed [200] v200
+insert_committed [200] v201 k1
+commit_any [200] v0
+commit_any [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.i201.test
new file mode 100644
index 00000000000..e8043f57581
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.i200.i201.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.d201.result
new file mode 100644
index 00000000000..f22da09c1c9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.d201.result
@@ -0,0 +1,3 @@
+insert_committed [0] v0 k1
+delete_committed [200] v0
+commit_any [200] v0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.d201.test
new file mode 100644
index 00000000000..a51a5c540f0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.d201.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+placeholder provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.i201.result
new file mode 100644
index 00000000000..75f2f8f18fb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.i201.result
@@ -0,0 +1,4 @@
+insert_committed [0] v0 k1
+delete_committed [200] v0
+insert_committed [200] v201 k1
+commit_any [200] v0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.i201.test
new file mode 100644
index 00000000000..14c521f84b3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.i0.p200.i201.test
@@ -0,0 +1,4 @@
+key k1
+insert committed 0 v0
+placeholder provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.d100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.d100.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.d100.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.d100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.d100.test
new file mode 100644
index 00000000000..14a3e4a0a05
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.d100.test
@@ -0,0 +1,4 @@
+live 100
+key k1
+delete committed 0
+delete provisional 100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.i100.result
new file mode 100644
index 00000000000..cf5ccdb6561
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.i100.result
@@ -0,0 +1,2 @@
+insert_provisional [100] v100 k1
+lock [100] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.i100.test
new file mode 100644
index 00000000000..9b2b7ebc3ce
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.d0.i100.test
@@ -0,0 +1,4 @@
+live 100
+key k1
+delete committed 0
+insert provisional 100 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.d100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.d100.result
new file mode 100644
index 00000000000..ac869f393c1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.d100.result
@@ -0,0 +1,3 @@
+insert_committed [0] v10 k1
+delete_provisional [100] v10
+lock [100] v10
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.d100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.d100.test
new file mode 100644
index 00000000000..e74f22b4c49
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.d100.test
@@ -0,0 +1,4 @@
+live 100
+key k1
+insert committed 0 v10
+delete provisional 100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.i100.result
new file mode 100644
index 00000000000..d5ef14e691b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.i100.result
@@ -0,0 +1,5 @@
+insert_committed [0] v10 k1
+delete_provisional [100] v10
+lock [100] v10
+insert_provisional [100] v100 k1
+lock [100] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.i100.test
new file mode 100644
index 00000000000..2a3ec8e5142
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live.i0.i100.test
@@ -0,0 +1,4 @@
+live 100
+key k1
+insert committed 0 v10
+insert provisional 100 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.d201.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.d201.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.d201.test
new file mode 100644
index 00000000000..e9c30ec58f6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.d201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+delete committed 0
+delete provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.i201.result
new file mode 100644
index 00000000000..2a2700cdbaf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.i201.result
@@ -0,0 +1,2 @@
+insert_provisional [200,201] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.i201.test
new file mode 100644
index 00000000000..585f7d24814
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.d200.i201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+delete committed 0
+delete provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.d201.result
new file mode 100644
index 00000000000..1fb6fb9f63e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.d201.result
@@ -0,0 +1,4 @@
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200,201] v200
+lock [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.d201.test
new file mode 100644
index 00000000000..f1b64294f1c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.d201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+delete committed 0
+insert provisional 200 v200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.i201.result
new file mode 100644
index 00000000000..8e7dcba5d86
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.i201.result
@@ -0,0 +1,6 @@
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200,201] v200
+lock [200] v200
+insert_provisional [200,201] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.i201.test
new file mode 100644
index 00000000000..b85cf241b72
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.i200.i201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+delete committed 0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.d201.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.d201.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.d201.test
new file mode 100644
index 00000000000..b687ed5e77f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.d201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+delete committed 0
+placeholder provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.i201.result
new file mode 100644
index 00000000000..2a2700cdbaf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.i201.result
@@ -0,0 +1,2 @@
+insert_provisional [200,201] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.i201.test
new file mode 100644
index 00000000000..b761f3520a0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.d0.p200.i201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+delete committed 0
+placeholder provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.d201.result
new file mode 100644
index 00000000000..015e131499a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.d201.result
@@ -0,0 +1,3 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.d201.test
new file mode 100644
index 00000000000..0d1a36fc03c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.d201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+insert committed 0 v0
+delete provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.i201.result
new file mode 100644
index 00000000000..7fbfc89f177
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.i201.result
@@ -0,0 +1,5 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200,201] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.i201.test
new file mode 100644
index 00000000000..c819d1d525a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.d200.i201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+insert committed 0 v0
+delete provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.d201.result
new file mode 100644
index 00000000000..461992b2d2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.d201.result
@@ -0,0 +1,7 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200,201] v200
+lock [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.d201.test
new file mode 100644
index 00000000000..ba4078dc791
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.d201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.i201.result
new file mode 100644
index 00000000000..e433f89451e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.i201.result
@@ -0,0 +1,9 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200,201] v200
+lock [200] v200
+insert_provisional [200,201] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.i201.test
new file mode 100644
index 00000000000..537bab1b91e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.i200.i201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.d201.result
new file mode 100644
index 00000000000..acd7f46e453
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.d201.result
@@ -0,0 +1,3 @@
+insert_committed [0] v0 k1
+delete_provisional [200,201] v0
+lock [200] v0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.d201.test
new file mode 100644
index 00000000000..5ca7424f8d1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.d201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+insert committed 0 v0
+placeholder provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.i201.result
new file mode 100644
index 00000000000..3ff4dbccb4f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.i201.result
@@ -0,0 +1,5 @@
+insert_committed [0] v0 k1
+delete_provisional [200,201] v0
+lock [200] v0
+insert_provisional [200,201] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.i201.test
new file mode 100644
index 00000000000..0e128ef6e80
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200,201.i0.p200.i201.test
@@ -0,0 +1,6 @@
+live 200
+live 201
+key k1
+insert committed 0 v0
+placeholder provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.commit202.i0.i200.i201.i202.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.commit202.i0.i200.i201.i202.result
new file mode 100644
index 00000000000..cbb863f36ab
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.commit202.i0.i200.i201.i202.result
@@ -0,0 +1,5 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v200 k1
+lock [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.commit202.i0.i200.i201.i202.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.commit202.i0.i200.i201.i202.test
new file mode 100644
index 00000000000..e7d60f9736b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.commit202.i0.i200.i201.i202.test
@@ -0,0 +1,8 @@
+xid 200 live
+xid 201 aborting
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+insert provisional 201 v201
+insert provisional 202 v202
+
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.i0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.i0.i200.i201.result
new file mode 100644
index 00000000000..cbb863f36ab
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.i0.i200.i201.result
@@ -0,0 +1,5 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v200 k1
+lock [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.i0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.i0.i200.i201.test
new file mode 100644
index 00000000000..9ad509e25a1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.abort201.i0.i200.i201.test
@@ -0,0 +1,6 @@
+xid 200 live
+xid 201 aborting
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.committing201.i0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.committing201.i0.i200.i201.result
new file mode 100644
index 00000000000..ab15d13fe29
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.committing201.i0.i200.i201.result
@@ -0,0 +1,9 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200] v200
+lock [200] v200
+insert_provisional [200] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.committing201.i0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.committing201.i0.i200.i201.test
new file mode 100644
index 00000000000..ba6775d37b3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.committing201.i0.i200.i201.test
@@ -0,0 +1,6 @@
+xid 200 live
+xid 201 committing
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.d201.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.d201.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.d201.test
new file mode 100644
index 00000000000..e8e205d8878
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.d201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+delete committed 0
+delete provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.i201.result
new file mode 100644
index 00000000000..58d40ca1756
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.i201.result
@@ -0,0 +1,2 @@
+insert_provisional [200] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.i201.test
new file mode 100644
index 00000000000..af95b8fb11a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.d200.i201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+delete committed 0
+delete provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.d201.result
new file mode 100644
index 00000000000..60da48d2ee8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.d201.result
@@ -0,0 +1,4 @@
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200] v200
+lock [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.d201.test
new file mode 100644
index 00000000000..7db0a9adf96
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.d201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+delete committed 0
+insert provisional 200 v200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.i201.result
new file mode 100644
index 00000000000..49884e70e93
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.i201.result
@@ -0,0 +1,6 @@
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200] v200
+lock [200] v200
+insert_provisional [200] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.i201.test
new file mode 100644
index 00000000000..5b2c9da5f5c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.i200.i201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+delete committed 0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.d201.result
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.d201.result
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.d201.test
new file mode 100644
index 00000000000..b773f437c53
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.d201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+delete committed 0
+placeholder provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.i201.result
new file mode 100644
index 00000000000..58d40ca1756
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.i201.result
@@ -0,0 +1,2 @@
+insert_provisional [200] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.i201.test
new file mode 100644
index 00000000000..360e4f0791f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.d0.p200.i201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+delete committed 0
+placeholder provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.d201.result
new file mode 100644
index 00000000000..015e131499a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.d201.result
@@ -0,0 +1,3 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.d201.test
new file mode 100644
index 00000000000..24486236d0c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.d201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+insert committed 0 v0
+delete provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.i201.result
new file mode 100644
index 00000000000..a7a31ffa44c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.i201.result
@@ -0,0 +1,5 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.i201.test
new file mode 100644
index 00000000000..40d13183082
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.d200.i201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+insert committed 0 v0
+delete provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.d201.result
new file mode 100644
index 00000000000..415c55130fa
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.d201.result
@@ -0,0 +1,7 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200] v200
+lock [200] v200
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.d201.test
new file mode 100644
index 00000000000..e7461d58a42
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.d201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.i201.result
new file mode 100644
index 00000000000..ab15d13fe29
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.i201.result
@@ -0,0 +1,9 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200] v200
+lock [200] v200
+insert_provisional [200] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.i201.test
new file mode 100644
index 00000000000..ed25c487477
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.i200.i201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+insert committed 0 v0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.d201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.d201.result
new file mode 100644
index 00000000000..015e131499a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.d201.result
@@ -0,0 +1,3 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.d201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.d201.test
new file mode 100644
index 00000000000..84f83e6e77d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.d201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+insert committed 0 v0
+placeholder provisional 200
+delete provisional 201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.i201.result
new file mode 100644
index 00000000000..a7a31ffa44c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.i201.result
@@ -0,0 +1,5 @@
+insert_committed [0] v0 k1
+delete_provisional [200] v0
+lock [200] v0
+insert_provisional [200] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.i201.test
new file mode 100644
index 00000000000..c3fbe42b219
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.live200.i0.p200.i201.test
@@ -0,0 +1,5 @@
+live 200
+key k1
+insert committed 0 v0
+placeholder provisional 200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i100.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i100.result
new file mode 100644
index 00000000000..cf5ccdb6561
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i100.result
@@ -0,0 +1,2 @@
+insert_provisional [100] v100 k1
+lock [100] v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i100.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i100.test
new file mode 100644
index 00000000000..1fc4dc60ce9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i100.test
@@ -0,0 +1,4 @@
+xid 100 preparing
+key k1
+delete committed 0
+insert provisional 100 v100
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i200.i201.result b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i200.i201.result
new file mode 100644
index 00000000000..49884e70e93
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i200.i201.result
@@ -0,0 +1,6 @@
+insert_provisional [200] v200 k1
+lock [200] v200
+delete_provisional [200] v200
+lock [200] v200
+insert_provisional [200] v201 k1
+lock [200] v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i200.i201.test b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i200.i201.test
new file mode 100644
index 00000000000..b7764174548
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-undo-do-tests/prov.preparing.d0.i200.i201.test
@@ -0,0 +1,5 @@
+xid 200 preparing
+key k1
+delete committed 0
+insert provisional 200 v200
+insert provisional 201 v201
diff --git a/storage/tokudb/PerconaFT/src/tests/hotindexer-with-queries.cc b/storage/tokudb/PerconaFT/src/tests/hotindexer-with-queries.cc
new file mode 100644
index 00000000000..a0be49c1617
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/hotindexer-with-queries.cc
@@ -0,0 +1,275 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "toku_config.h"
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "key-val.h"
+
+enum {NUM_INDEXER_INDEXES=1};
+static const int NUM_DBS = NUM_INDEXER_INDEXES + 1; // 1 for source DB
+static const int NUM_ROWS = 1000000;
+int num_rows;
+typedef enum {FORWARD = 0, BACKWARD} Direction;
+typedef enum {TXN_NONE = 0, TXN_CREATE = 1, TXN_END = 2} TxnWork;
+
+DB_ENV *env;
+
+/*
+ * client scans the primary table (like a range query)
+ */
+
+static void * client(void *arg)
+{
+ DB *src = (DB *)arg;
+ if ( verbose ) printf("client start\n");
+
+ int r;
+ struct timeval start, now;
+ DB_TXN *txn;
+ DBT key, val;
+ uint32_t k, v;
+
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DBC *cursor;
+ r = src->cursor(src, txn, &cursor, 0); CKERR(r);
+
+ int row = 0;
+
+ gettimeofday(&start,0);
+ while ( r != DB_NOTFOUND ) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if ( r != DB_NOTFOUND ) {
+ row++;
+ }
+ }
+ gettimeofday(&now, 0);
+ if ( verbose ) printf("client : primary table scanned in %d sec, contains %d rows\n",
+ (int)(now.tv_sec - start.tv_sec),
+ row);
+
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ if ( verbose ) printf("client done\n");
+
+ return NULL;
+}
+
+toku_pthread_t *client_thread;
+static void client_init(void)
+{
+ client_thread = (toku_pthread_t *)toku_malloc(sizeof(toku_pthread_t));
+}
+
+static void client_cleanup(void)
+{
+ toku_free(client_thread); client_thread = NULL;
+}
+
+static void query_only(DB *src)
+{
+ int r;
+ void *t0;
+ client_init();
+
+ // start thread doing query
+ r = toku_pthread_create(client_thread, 0, client, (void *)src);
+ CKERR(r);
+
+ r = toku_pthread_join(*client_thread, &t0);
+ CKERR(r);
+
+ client_cleanup();
+}
+
+static void test_indexer(DB *src, DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_INDEXER *indexer;
+ uint32_t db_flags[NUM_DBS];
+
+ if ( verbose ) printf("test_indexer\n");
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = 0;
+ }
+
+ client_init();
+
+ // create and initialize indexer
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("test_indexer create_indexer\n");
+ r = env->create_indexer(env, txn, &indexer, src, NUM_DBS-1, &dbs[1], db_flags, 0);
+ CKERR(r);
+ r = indexer->set_error_callback(indexer, NULL, NULL);
+ CKERR(r);
+ r = indexer->set_poll_function(indexer, poll_print, NULL);
+ CKERR(r);
+
+ // start thread doing query
+ r = toku_pthread_create(client_thread, 0, client, (void *)src); CKERR(r);
+
+ struct timeval start, now;
+ if ( verbose ) {
+ printf("test_indexer build\n");
+ gettimeofday(&start,0);
+ }
+ r = indexer->build(indexer);
+ CKERR(r);
+ if ( verbose ) {
+ gettimeofday(&now,0);
+ int duration = (int)(now.tv_sec - start.tv_sec);
+ if ( duration > 0 )
+ printf("test_indexer build : sec = %d\n", duration);
+ }
+
+ void *t0;
+ r = toku_pthread_join(*client_thread, &t0); CKERR(r);
+
+ if ( verbose ) printf("test_indexer close\n");
+ r = indexer->close(indexer);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC);
+ CKERR(r);
+
+ client_cleanup();
+}
+
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logname[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(logname, 2, TOKU_TEST_FILENAME, "log"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log"); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ generate_permute_tables();
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ int ids[MAX_DBS];
+ DB *dbs[MAX_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ ids[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &ids[i];
+ char key_name[32];
+ sprintf(key_name, "key%d", i);
+ r = dbs[i]->open(dbs[i], NULL, key_name, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ // generate the src DB (do not use put_multiple)
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = generate_initial_table(dbs[0], txn, num_rows); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ // scan the whole table twice to reduce possible flattening effects
+ // -------------------------- //
+ query_only(dbs[0]);
+ query_only(dbs[0]);
+ // -------------------------- //
+
+ // scan the whole table while running the indexer
+ // -------------------------- //
+ test_indexer(dbs[0], dbs);
+ // -------------------------- //
+
+ // scan the whole table again to confirm performance
+ // -------------------------- //
+ query_only(dbs[0]);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+ r = env->close(env, 0); CKERR(r);
+
+ if ( verbose && (r == 0)) printf("PASS\n");
+}
+
+// ------------ infrastructure ----------
+
+static inline void
+do_args (int argc, char * const argv[]) {
+ const char *progname=argv[0];
+ num_rows = NUM_ROWS;
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0],"-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose=0;
+ } else if (strcmp(argv[0],"-r")==0) {
+ argc--; argv++;
+ num_rows = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Usage:\n %s [-v] [-q] [-r rows]\n", progname);
+ exit(1);
+ }
+ argc--; argv++;
+ }
+}
+
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/inflate.cc b/storage/tokudb/PerconaFT/src/tests/inflate.cc
new file mode 100644
index 00000000000..c1143873217
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/inflate.cc
@@ -0,0 +1,171 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Idea: inflate a node by
+ * create a 2-level tree
+ * Nodes are A B C D E F G H
+ * Fill them up sequentially so they'll all be near 4MB.
+ * Close the file
+ * Insert some more to H (buffered in the root)
+ * Delete stuff from G (so that H merges with G)
+ * G ends up too big.
+ */
+
+#include "test.h"
+
+DB_ENV *env;
+DB *db;
+const char dbname[] = "foo.db";
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_PRIVATE|DB_INIT_TXN;
+
+static void
+open_em (void)
+{
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+}
+
+static void
+close_em (void)
+{
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void
+reopen_em (void)
+{
+ close_em();
+ open_em();
+}
+
+
+static void
+setup(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 8192); CKERR(r);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+}
+
+char vdata[150];
+
+static void
+insert_n (uint32_t ah) {
+ uint32_t an = htonl(ah);
+ DBT key;
+ dbt_init(&key, &an, 4);
+ DBT val;
+ dbt_init(&val, vdata, sizeof vdata);
+ int r = db->put(db, NULL, &key, &val, 0);
+ CKERR(r);
+}
+
+static void
+delete_n (uint32_t ah)
+{
+ uint32_t an = htonl(ah);
+ DBT key;
+ dbt_init(&key, &an, 4);
+ int r = db->del(db, NULL, &key, DB_DELETE_ANY);
+ CKERR(r);
+}
+
+static void
+get_n (uint32_t ah, int expect_r)
+{
+ uint32_t an = htonl(ah);
+ DBT key;
+ dbt_init(&key, &an, 4);
+ DBT val;
+ dbt_init_malloc(&val);
+ int r = db->get(db, NULL, &key, &val, 0);
+ assert(r==expect_r);
+ if (r==0) toku_free(val.data);
+}
+
+static void
+doit (void)
+{
+ uint32_t N=100;
+ for (uint32_t i=0; i<N; i++) {
+ insert_n(i<<16);
+ }
+ reopen_em();
+ for (uint32_t j=0; j<46; j++) {
+ insert_n(('.'<<16) + 1 +j);
+ }
+ for (uint32_t i=N-1; i<N; i++) {
+ delete_n(i<<16);
+ get_n(i<<16, DB_NOTFOUND);
+ }
+ reopen_em();
+ insert_n(N<<16);
+ get_n(N<<16, 0);
+ reopen_em();
+ for (uint32_t i='J'; i<N+1; i++) {
+ delete_n(i<<16);
+ get_n(i<<16, DB_NOTFOUND);
+ }
+ reopen_em();
+ reopen_em();
+ for (uint32_t j=0; j<46; j++) {
+ insert_n(('.'<<16) + 1 +j +46);
+ }
+ for (uint32_t i=0; i<13; i++) {
+ delete_n((73 - i)<< 16);
+ get_n((73-i) << 16, DB_NOTFOUND);
+ }
+ reopen_em(); // now a node is 9143 bytes
+}
+
+int test_main (int argc __attribute__((__unused__)), char * const argv[] __attribute__((__unused__))) {
+ setup();
+ doit();
+ close_em();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/inflate2.cc b/storage/tokudb/PerconaFT/src/tests/inflate2.cc
new file mode 100644
index 00000000000..af4d9d50835
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/inflate2.cc
@@ -0,0 +1,161 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Idea: inflate a node by
+ * create a 2-level tree
+ * Nodes are A B C D E F G H
+ * Fill them up sequentially so they'll all be near 4MB.
+ * Close the file
+ * Insert some more to H (buffered in the root)
+ * Delete stuff from G (so that H merges with G)
+ * G ends up too big (but it's not the merge)
+ */
+
+#include "test.h"
+
+DB_ENV *env;
+DB *db;
+const char dbname[] = "foo.db";
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_PRIVATE|DB_INIT_TXN;
+
+static void
+open_em (void)
+{
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+}
+
+static void
+close_em (void)
+{
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void
+reopen_em (void)
+{
+ close_em();
+ open_em();
+}
+
+
+static void
+setup(void)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 8192); CKERR(r);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+}
+
+char vdata[150];
+
+static void
+insert_n (uint32_t ah) {
+ uint32_t an = htonl(ah);
+ DBT key;
+ dbt_init(&key, &an, 4);
+ DBT val;
+ dbt_init(&val, vdata, sizeof vdata);
+ int r = db->put(db, NULL, &key, &val, 0);
+ CKERR(r);
+}
+
+static void
+get_n (uint32_t ah, int expect_r)
+{
+ uint32_t an = htonl(ah);
+ DBT key;
+ dbt_init(&key, &an, 4);
+ DBT val;
+ dbt_init_malloc(&val);
+ int r = db->get(db, NULL, &key, &val, 0);
+ assert(r==expect_r);
+ if (r==0) toku_free(val.data);
+}
+
+
+static void
+delete_n_now (uint32_t ah)
+{
+ uint32_t an = htonl(ah);
+ DBT key;
+ dbt_init(&key, &an, 4);
+ int r = db->del(db, NULL, &key, DB_DELETE_ANY);
+ CKERR(r);
+ get_n(ah, DB_NOTFOUND);
+}
+
+static void
+doit (void)
+{
+ uint32_t N=46;
+ uint32_t BIG=1<<16;
+ for (uint32_t i=0; i<2*N; i++) {
+ insert_n(i<<8);
+ }
+ insert_n(BIG);
+ insert_n(BIG+1);
+ reopen_em();
+ for (uint32_t i=0; i<N; i++) {
+ insert_n((2*N+i)<<8);
+ }
+ delete_n_now(BIG+1);
+ reopen_em();
+ for (uint32_t i=0; i<N; i++) {
+ insert_n((48<<8) + i);
+ }
+ insert_n((48<<8) + N);
+ return;
+}
+
+int test_main (int argc __attribute__((__unused__)), char * const argv[] __attribute__((__unused__))) {
+ setup();
+ doit();
+ close_em();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/insert-dup-prelock.cc b/storage/tokudb/PerconaFT/src/tests/insert-dup-prelock.cc
new file mode 100644
index 00000000000..87733c906a2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/insert-dup-prelock.cc
@@ -0,0 +1,171 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <toku_byteswap.h>
+#include <portability/toku_path.h>
+
+static int verbose = 0;
+static uint64_t maxk = 100000;
+
+static int usage(const char *prog) {
+ fprintf(stderr, "%s: run single row insertions with prelocking\n", prog);
+ fprintf(stderr, "[--n %" PRIu64 "]\n", maxk);
+ return 1;
+}
+
+static int inserter(DB_ENV *env, DB *db, uint64_t _maxk, int putflags, int expectr) {
+ if (verbose) printf("%p %p\n", env, db);
+ int r;
+ for (uint64_t k = 0; k < _maxk; k++) {
+
+ if (verbose) printf("%" PRIu64 "\n", k);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ assert(r == 0);
+
+ r = db->pre_acquire_table_lock(db, txn);
+ assert(r == 0);
+
+ uint64_t kk = bswap_64(k);
+ DBT key = { .data = &kk, .size = sizeof kk };
+ DBT val = { .data = &k, .size = sizeof k };
+ r = db->put(db, txn, &key, &val, putflags);
+ assert(r == expectr);
+
+ r = txn->commit(txn, DB_TXN_NOSYNC);
+ assert(r == 0);
+ }
+
+ return 0;
+}
+
+static int env_init(DB_ENV **envptr, const char *envdir) {
+ int r;
+ DB_ENV *env;
+
+ r = db_env_create(&env, 0);
+ if (r == 0) {
+ // env setup
+
+ // env open
+ r = env->open(env, envdir, DB_CREATE+DB_PRIVATE+DB_INIT_LOCK+DB_INIT_LOG+DB_INIT_MPOOL+DB_INIT_TXN, 0777);
+ }
+ if (r == 0)
+ *envptr = env;
+ return r;
+}
+
+static int db_init(DB_ENV *env, const char *dbname, DB **dbptr) {
+ int r;
+ DB *db;
+
+ r = db_create(&db, env, 0);
+ if (r == 0) {
+ // db create
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_CREATE, 0777);
+ if (r != 0) {
+ r = db->close(db, 0);
+ assert(r == 0);
+ }
+ }
+ if (r == 0)
+ *dbptr = db;
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ for (int i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ if (strcmp(arg, "--n") == 0 && i+1 < argc) {
+ maxk = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+
+ return usage(argv[0]);
+ }
+
+ const char *envdir = TOKU_TEST_FILENAME;
+ char cmd[TOKU_PATH_MAX+sizeof("rm -rf ")];
+ snprintf(cmd, sizeof(cmd), "rm -rf %s", TOKU_TEST_FILENAME);
+ r = system(cmd);
+ assert(r == 0);
+ r = mkdir(envdir, 0777);
+ assert(r == 0);
+
+ DB_ENV *env;
+ r = env_init(&env, envdir);
+ assert(r == 0);
+
+ DB *db;
+ r = db_init(env, "db0", &db);
+ assert(r == 0);
+
+ r = inserter(env, db, maxk, 0, 0);
+ assert(r == 0);
+
+ r = inserter(env, db, maxk, DB_NOOVERWRITE, DB_KEYEXIST);
+ assert(r == 0);
+
+ r = db->close(db, 0);
+ assert(r == 0);
+
+ r = env->close(env, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/ipm.py b/storage/tokudb/PerconaFT/src/tests/ipm.py
new file mode 100755
index 00000000000..84dfee5492e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/ipm.py
@@ -0,0 +1,61 @@
+#!/usr/local/bin/python2.6
+
+import sys
+import os
+import pexpect
+import getpass
+
+#
+# remote_cmd
+#
+
+nameaddr='admn@192.168.1.254'
+passwd='admn'
+
+def IPM_cmd(cmds):
+ # password handling
+ ssh_newkey = 'Are you sure you want to continue connecting'
+ p=pexpect.spawn('ssh %s' % nameaddr, timeout=60)
+ i=p.expect([ssh_newkey,'Password:',pexpect.EOF])
+ if i==0:
+ p.sendline('yes')
+ i=p.expect([ssh_newkey,'Password:',pexpect.EOF])
+ if i==1:
+ p.sendline(passwd)
+ elif i==2:
+ print "I either got key or connection timeout"
+ pass
+
+ # run command(s)
+ i = p.expect('Sentry:')
+ for cmd in cmds:
+ if i==0:
+ p.sendline(cmd)
+ else:
+ print 'p.expect saw', p.before
+ i = p.expect('Sentry:')
+ print p.before
+
+ # close session
+ p.sendline('quit')
+ p.expect(pexpect.EOF)
+ return 0
+
+def IPM_power_on():
+ IPM_cmd(['on all'])
+
+def IPM_power_off():
+ IPM_cmd(['off all'])
+
+def main(argv):
+# passwd = getpass.getpass('password for %s:' % (nameaddr))
+ if argv[1] == 'on':
+ IPM_power_on()
+ elif argv[1] == 'off':
+ IPM_power_off()
+ else:
+ IPM_cmd(argv[1:])
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/storage/tokudb/PerconaFT/src/tests/isolation-read-committed.cc b/storage/tokudb/PerconaFT/src/tests/isolation-read-committed.cc
new file mode 100644
index 00000000000..ab0905d8c3c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/isolation-read-committed.cc
@@ -0,0 +1,161 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that isolation works right for subtransactions.
+// In particular, check to see what happens if a subtransaction has different isolation level from its parent.
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ {
+ DB_TXN *txna;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ DBT key,val;
+ r = db->put(db, txna, dbt_init(&key, "a", 2), dbt_init(&val, "a", 2), 0); CKERR(r);
+
+ r = txna->commit(txna, 0); CKERR(r);
+ }
+ DB_TXN *txn_put, *txn_committed, *txn_uncommitted;
+ r = env->txn_begin(env, NULL, &txn_put, DB_READ_COMMITTED); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn_committed, DB_READ_COMMITTED); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn_uncommitted, DB_READ_UNCOMMITTED); CKERR(r);
+
+ //
+ // test a simple get
+ //
+ {
+ DBT key,val;
+ r = db->put(db, txn_put, dbt_init(&key, "x", 2), dbt_init(&val, "x", 2), 0); CKERR(r);
+ dbt_init_malloc(&val);
+ r = db->get(db, txn_put, dbt_init(&key, "x", 2), &val, 0); CKERR(r);
+ toku_free(val.data);
+
+ dbt_init_malloc(&val);
+ r = db->get(db, txn_committed, dbt_init(&key, "x", 2), &val, 0); CKERR2(r, DB_NOTFOUND);
+ toku_free(val.data);
+
+ dbt_init_malloc(&val);
+ r = db->get(db, txn_uncommitted, dbt_init(&key, "x", 2), &val, 0); CKERR(r);
+ toku_free(val.data);
+
+ r = db->del(db, txn_put, dbt_init(&key, "a", 2), 0); CKERR(r);
+
+ dbt_init_malloc(&val);
+ r = db->get(db, txn_put, dbt_init(&key, "a", 2), &val, 0); CKERR2(r, DB_NOTFOUND);
+ toku_free(val.data);
+
+ dbt_init_malloc(&val);
+ r = db->get(db, txn_committed, dbt_init(&key, "a", 2), &val, 0); CKERR(r);
+ toku_free(val.data);
+
+ dbt_init_malloc(&val);
+ r = db->get(db, txn_uncommitted, dbt_init(&key, "a", 2), &val, 0); CKERR2(r, DB_NOTFOUND);
+ toku_free(val.data);
+
+ val.data = NULL;
+ }
+
+
+ r = txn_put->commit(txn_put, 0); CKERR(r);
+ r = txn_committed->commit(txn_committed, 0); CKERR(r);
+ r = txn_uncommitted->commit(txn_uncommitted, 0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn_put, DB_READ_COMMITTED); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn_committed, DB_READ_COMMITTED); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn_uncommitted, DB_READ_UNCOMMITTED); CKERR(r);
+
+ //
+ // test a simple get
+ //
+ {
+ DBT key,val;
+ DBT curr_key, curr_val;
+ DBC* cursor_committed = NULL;
+ DBC* cursor_uncommitted = NULL;
+ memset(&curr_key, 0, sizeof(curr_key));
+ memset(&curr_val, 0, sizeof(curr_val));
+
+ r = db->cursor(db, txn_committed, &cursor_committed, 0); assert(r == 0);
+ r = db->cursor(db, txn_uncommitted, &cursor_uncommitted, 0); assert(r == 0);
+
+ r = db->put(db, txn_put, dbt_init(&key, "y", 2), dbt_init(&val, "y", 2), 0); CKERR(r);
+
+ r = cursor_uncommitted->c_get(cursor_uncommitted, &curr_key, &curr_val, DB_NEXT); CKERR(r);
+ assert(((char *)(curr_key.data))[0] == 'x');
+ assert(((char *)(curr_val.data))[0] == 'x');
+
+ r = cursor_committed->c_get(cursor_committed, &curr_key, &curr_val, DB_NEXT); CKERR(r);
+ assert(((char *)(curr_key.data))[0] == 'x');
+ assert(((char *)(curr_val.data))[0] == 'x');
+
+
+
+ r = cursor_committed->c_get(cursor_committed, &curr_key, &curr_val, DB_NEXT); CKERR2(r, DB_NOTFOUND);
+ r = cursor_uncommitted->c_get(cursor_uncommitted, &curr_key, &curr_val, DB_NEXT); CKERR(r);
+ assert(((char *)(curr_key.data))[0] == 'y');
+ assert(((char *)(curr_val.data))[0] == 'y');
+
+ cursor_committed->c_close(cursor_committed);
+ cursor_uncommitted->c_close(cursor_uncommitted);
+ }
+ r = txn_put->commit(txn_put, 0); CKERR(r);
+ r = txn_committed->commit(txn_committed, 0); CKERR(r);
+ r = txn_uncommitted->commit(txn_uncommitted, 0); CKERR(r);
+
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/isolation.cc b/storage/tokudb/PerconaFT/src/tests/isolation.cc
new file mode 100644
index 00000000000..abed04f1600
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/isolation.cc
@@ -0,0 +1,94 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that isolation works right for subtransactions.
+// In particular, check to see what happens if a subtransaction has different isolation level from its parent.
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ {
+ DB_TXN *txna;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ DBT key,val;
+ r = db->put(db, txna, dbt_init(&key, "a", 2), dbt_init(&val, "a", 2), 0); CKERR(r);
+
+ r = txna->commit(txna, 0); CKERR(r);
+ }
+ DB_TXN *txna, *txnx;
+ r = env->txn_begin(env, NULL, &txna, DB_READ_UNCOMMITTED); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnx, 0); CKERR(r);
+
+ // X writes a value, and B tries to read it in uncommitted
+ {
+// DB_TXN *txnb;
+// r = env->txn_begin(env, txna, &txnb, DB_READ_UNCOMMITTED); CKERR(r);
+ {
+ DBT key,val;
+ r = db->put(db, txnx, dbt_init(&key, "x", 2), dbt_init(&val, "x", 2), 0); CKERR(r);
+ dbt_init_malloc(&val);
+ r = db->get(db, txna, dbt_init(&key, "x", 2), &val, 0); CKERR(r);
+ toku_free(val.data);
+ val.data = NULL;
+ }
+// r = txnb->commit(txnb, 0); CKERR(r);
+ }
+ r = txna->commit(txna, 0); CKERR(r);
+ r = txnx->commit(txnx, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/key-val.h b/storage/tokudb/PerconaFT/src/tests/key-val.h
new file mode 100644
index 00000000000..294f53048ed
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/key-val.h
@@ -0,0 +1,245 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+//
+// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
+//
+
+// To use, during initialization:
+// generate_permute_tables();
+// r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+//
+
+
+enum {MAX_DBS=32};
+enum {MAGIC=311};
+
+// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
+// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
+int a[MAX_DBS][32];
+int inv[MAX_DBS][32];
+
+// rotate right and left functions
+static inline uint32_t UU() rotr32(const uint32_t x, const uint32_t num) {
+ const uint32_t n = num % 32;
+ return (x >> n) | ( x << (32 - n));
+}
+static inline uint64_t UU() rotr64(const uint64_t x, const uint64_t num) {
+ const uint64_t n = num % 64;
+ return ( x >> n ) | ( x << (64 - n));
+}
+static inline uint32_t UU() rotl32(const uint32_t x, const uint32_t num) {
+ const uint32_t n = num % 32;
+ return (x << n) | ( x >> (32 - n));
+}
+static inline uint64_t UU() rotl64(const uint64_t x, const uint64_t num) {
+ const uint64_t n = num % 64;
+ return ( x << n ) | ( x >> (64 - n));
+}
+
+static void UU() generate_permute_tables(void) {
+ int i, j, tmp;
+ for(int db=0;db<MAX_DBS;db++) {
+ for(i=0;i<32;i++) {
+ a[db][i] = i;
+ }
+ for(i=0;i<32;i++) {
+ j = random() % (i + 1);
+ tmp = a[db][j];
+ a[db][j] = a[db][i];
+ a[db][i] = tmp;
+ }
+// if(db < NUM_DBS){ printf("a[%d] = ", db); for(i=0;i<32;i++) { printf("%2d ", a[db][i]); } printf("\n");}
+ for(i=0;i<32;i++) {
+ inv[db][a[db][i]] = i;
+ }
+ }
+}
+
+// permute bits of x based on permute table bitmap
+static uint32_t UU() twiddle32(uint32_t x, int db)
+{
+ uint32_t b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << a[db][i];
+ }
+ return b;
+}
+
+// permute bits of x based on inverse permute table bitmap
+static uint32_t UU() inv_twiddle32(uint32_t x, int db)
+{
+ uint32_t b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << inv[db][i];
+ }
+ return b;
+}
+
+// generate val from key, index
+static uint32_t UU() generate_val(int key, int i) {
+ return rotl32((key + MAGIC), i);
+}
+static uint32_t UU() pkey_for_val(int key, int i) {
+ return rotr32(key, i) - MAGIC;
+}
+
+// There is no handlerton in this test, so this function is a local replacement
+// for the handlerton's generate_row_for_put().
+static int UU() put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ (void) src_db;
+ (void) src_val;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ assert(which != 0);
+ assert(dest_db != src_db);
+ {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(uint32_t)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(uint32_t));
+ dest_key->ulen = sizeof(uint32_t);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < sizeof(uint32_t)) {
+ dest_val->data = toku_xrealloc(dest_val->data, sizeof(uint32_t));
+ dest_val->ulen = sizeof(uint32_t);
+ }
+ uint32_t *new_key = (uint32_t *)dest_key->data;
+ uint32_t *new_val = (uint32_t *)dest_val->data;
+
+ *new_key = twiddle32(*(uint32_t*)src_key->data, which);
+ *new_val = generate_val(*(uint32_t*)src_key->data, which);
+
+ dest_key->size = sizeof(uint32_t);
+ dest_val->size = sizeof(uint32_t);
+ //data is already set above
+ }
+
+// printf("pmg : dest_key.data = %u, dest_val.data = %u \n", *(unsigned int*)dest_key->data, *(unsigned int*)dest_val->data);
+
+ return 0;
+}
+
+UU()
+static int put_multiple_generate_switch(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+
+ (void) src_db;
+
+ uint32_t which = (uint32_t) (intptr_t) dest_db->app_private;
+ assert(which == 0);
+
+ // switch the key and val
+ dbt_init(dest_key, src_val->data, src_val->size);
+ dbt_init(dest_val, src_key->data, src_key->size);
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+static int UU() uint_cmp(const void *ap, const void *bp) {
+ unsigned int an = *(unsigned int *)ap;
+ unsigned int bn = *(unsigned int *)bp;
+ if (an < bn)
+ return -1;
+ if (an > bn)
+ return +1;
+ return 0;
+}
+
+float last_progress = 0.0;
+static int UU() poll_print(void *extra, float progress) {
+ if ( verbose ) {
+ if ( last_progress + 0.01 < progress ) {
+ printf(" progress : %3.0f%%\n", progress * 100.0);
+ last_progress = progress;
+ }
+ }
+ (void) extra;
+ return 0;
+}
+
+enum {MAX_CLIENTS=10};
+static inline UU() uint32_t key_to_put(int iter, int offset)
+{
+ return (uint32_t)(((iter+1) * MAX_CLIENTS) + offset);
+}
+
+static int UU() generate_initial_table(DB *db, DB_TXN *txn, uint32_t rows)
+{
+ struct timeval start, now;
+ if ( verbose ) {
+ printf("generate_initial_table\n");
+ gettimeofday(&start,0);
+ }
+ int r = 0;
+ DBT key, val;
+ uint32_t k, v, i;
+ // create keys of stride MAX_CLIENTS
+ for (i=0; i<rows; i++)
+ {
+ k = key_to_put(i, 0);
+ v = generate_val(k, 0);
+ dbt_init(&key, &k, sizeof(k));
+ dbt_init(&val, &v, sizeof(v));
+ r = db->put(db, txn, &key, &val, 0);
+ if ( r != 0 ) break;
+ }
+ if ( verbose ) {
+ gettimeofday(&now,0);
+ int duration = (int)(now.tv_sec - start.tv_sec);
+ if ( duration > 0 )
+ printf("generate_initial_table : %u rows in %d sec = %d rows/sec\n", rows, duration, rows/duration);
+ }
+
+ return r;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/keyrange-merge.cc b/storage/tokudb/PerconaFT/src/tests/keyrange-merge.cc
new file mode 100644
index 00000000000..4c491e8404a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/keyrange-merge.cc
@@ -0,0 +1,234 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that key_range64 returns reasonable results after leaf merges
+
+// create a tree with at least 2 child nodes and large rows.
+// replace the rows with small rows.
+// this should cause a leaf node merge.
+// verify stats after the merge.
+
+#include <db.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static DB_ENV *env = NULL;
+static DB_TXN *txn = NULL;
+static DB *db = NULL;
+static uint32_t db_page_size = 4096;
+static uint32_t db_basement_size = 4096;
+static const char *envdir = TOKU_TEST_FILENAME;
+static uint64_t nrows = 0;
+
+static uint64_t
+max64(uint64_t a, uint64_t b) {
+ return a < b ? b : a;
+}
+
+static void
+run_test(void) {
+ if (verbose) printf("%s %" PRIu64 "\n", __FUNCTION__, nrows);
+
+ // create a tree with 2 children
+ uint32_t key_size = 9;
+ uint32_t val_size = db_basement_size / 32;
+ size_t est_row_size_with_overhead = 8 + key_size + 4 + val_size + 4; // xid + key + key_len + val + val)len
+ size_t rows_per_basement = db_basement_size / est_row_size_with_overhead;
+
+ if (nrows == 0)
+ nrows = 2 * (db_page_size / est_row_size_with_overhead);
+
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->open(env, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, db_page_size);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // insert keys 1, 3, 5, ... 2*(nrows-1) + 1
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (uint64_t i=0; i<nrows; i++) {
+ char key[100];
+ snprintf(key, sizeof key, "%08llu", (unsigned long long)2*i+1);
+ char val[val_size];
+ memset(val, 0, val_size);
+ DBT k = { .data = key, .size = key_size };
+ DBT v = { .data = val, .size = val_size };
+ r = db->put(db, txn, &k, &v, 0); CKERR(r);
+ }
+
+ DB_BTREE_STAT64 s64;
+ r = db->stat64(db, txn, &s64); CKERR(r);
+ if (verbose)
+ printf("stats %" PRId64 " %" PRId64 "\n", s64.bt_nkeys, s64.bt_dsize);
+ assert(0 < s64.bt_nkeys && s64.bt_nkeys <= nrows);
+ assert(0 < s64.bt_dsize && s64.bt_dsize <= nrows * (key_size + val_size));
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // lose the seqinsert bit by flushing the tree from the cache table
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // replace the rows with small values. this should shrink the leaf node and induce merging.
+ // do this until a leaf node merge occurs.
+ int t;
+ for (t = 0; t<100; t++) {
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ // replace in reverse order to disable the sequential insertion code
+ for (uint64_t i=nrows; i>0; i--) {
+ char key[100];
+ snprintf(key, sizeof key, "%08llu", (unsigned long long)2*(i-1)+1);
+ assert(1+strlen(key) == key_size);
+ DBT k;
+ dbt_init(&k, key, 1+strlen(key));
+ DBT v;
+ dbt_init(&v, NULL, 0);
+ r = db->put(db, txn, &k, &v, 0); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+
+ uint64_t merge_leaf = get_engine_status_val(env, "FT_FLUSHER_MERGE_LEAF");
+ if (merge_leaf > 0) {
+ if (verbose) printf("t=%d\n", t);
+ break;
+ }
+ }
+ assert(t < 100); // if this asserts, then no leaf merge occurred
+
+ // verify key_range for keys that exist in the tree
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (uint64_t i=0; i<nrows; i++) {
+ char key[100];
+ snprintf(key, 100, "%08llu", (unsigned long long)2*i+1);
+ DBT k;
+ uint64_t less,equal,greater;
+ int is_exact;
+ r = db->key_range64(db, txn, dbt_init(&k, key, 1+strlen(key)), &less, &equal, &greater, &is_exact); CKERR(r);
+ if (verbose)
+ printf("key %llu/%llu %llu %llu %llu %llu\n", (unsigned long long)2*i, (unsigned long long)2*nrows, (unsigned long long)less, (unsigned long long)equal, (unsigned long long)greater,
+ (unsigned long long)(less+equal+greater));
+ assert(is_exact == 0);
+ assert(0 < less + equal + greater);
+ assert(less + equal + greater < 2*nrows);
+ assert(equal == 1);
+ uint64_t est_i = max64(i, i + rows_per_basement/2);
+ assert(less <= est_i + est_i / 1);
+ assert(greater <= nrows - i + rows_per_basement/2);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // verify key range for keys that do not exist in the tree
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (uint64_t i=0; i<1+nrows; i++) {
+ char key[100];
+ snprintf(key, 100, "%08llu", (unsigned long long)2*i);
+ DBT k;
+ uint64_t less,equal,greater;
+ int is_exact;
+ r = db->key_range64(db, txn, dbt_init(&k, key, 1+strlen(key)), &less, &equal, &greater, &is_exact); CKERR(r);
+ if (verbose)
+ printf("key %llu/%llu %llu %llu %llu %llu\n", (unsigned long long)2*i, (unsigned long long)2*nrows, (unsigned long long)less, (unsigned long long)equal, (unsigned long long)greater,
+ (unsigned long long)(less+equal+greater));
+ assert(is_exact == 0);
+ assert(0 < less + equal + greater);
+ assert(less + equal + greater < 2*nrows);
+ assert(equal == 0);
+ uint64_t est_i = max64(i, i + rows_per_basement/2);
+ assert(less <= est_i + est_i / 1);
+ assert(greater <= nrows - i + rows_per_basement/2);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static int
+usage(void) {
+ fprintf(stderr, "-v (verbose)\n");
+ fprintf(stderr, "-q (quiet)\n");
+ fprintf(stderr, "--envdir %s\n", envdir);
+ fprintf(stderr, "--nrows %" PRIu64 " (number of rows)\n", nrows);
+ fprintf(stderr, "--nrows %" PRIu64 " (number of rows)\n", nrows);
+ return 1;
+}
+
+int
+test_main (int argc , char * const argv[]) {
+ for (int i = 1 ; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--envdir") == 0 && i+1 < argc) {
+ envdir = argv[++i];
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ return usage();
+ }
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ int r;
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ run_test();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/keyrange.cc b/storage/tokudb/PerconaFT/src/tests/keyrange.cc
new file mode 100644
index 00000000000..13567600cb1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/keyrange.cc
@@ -0,0 +1,336 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that key_range64 returns reasonable results after inserting rows into a tree.
+// variations include:
+// 1. trickle load versus bulk load
+// 2. sequential keys versus random keys
+// 3. basements on disk versus basements in memory
+
+#include <db.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static DB_ENV *env = NULL;
+static DB_TXN *txn = NULL;
+static DB *db = NULL;
+static uint32_t db_page_size = 4096;
+static uint32_t db_basement_size = 4096;
+static const char *envdir = TOKU_TEST_FILENAME;
+static uint64_t nrows = 30000;
+static bool get_all = true;
+static bool use_loader = false;
+static bool random_keys = false;
+
+static int
+my_compare(DB *this_db UU(), const DBT *a UU(), const DBT *b UU()) {
+ assert(a->size == b->size);
+ return memcmp(a->data, b->data, a->size);
+}
+
+static int
+my_generate_row(DB *dest_db UU(), DB *src_db UU(), DBT_ARRAY *dest_keys UU(), DBT_ARRAY *dest_vals UU(), const DBT *src_key UU(), const DBT *src_val UU()) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ assert(dest_key->flags == DB_DBT_REALLOC);
+ dest_key->data = toku_realloc(dest_key->data, src_key->size);
+ memcpy(dest_key->data, src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ assert(dest_val->flags == DB_DBT_REALLOC);
+ dest_val->data = toku_realloc(dest_val->data, src_val->size);
+ memcpy(dest_val->data, src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+static void
+swap(uint64_t keys[], uint64_t i, uint64_t j) {
+ uint64_t t = keys[i]; keys[i] = keys[j]; keys[j] = t;
+}
+
+static uint64_t
+max64(uint64_t a, uint64_t b) {
+ return a < b ? b : a;
+}
+
+static void open_env(void) {
+ int r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, my_generate_row); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+}
+
+static void
+run_test(void) {
+ if (verbose) printf("%s %" PRIu64 "\n", __FUNCTION__, nrows);
+
+ size_t key_size = 9;
+ size_t val_size = 9;
+ size_t est_row_size_with_overhead = 8 + key_size + 4 + val_size + 4 + 5; // xid + key + key_len + val + val_len + mvcc overhead
+ size_t rows_per_basement = db_basement_size / est_row_size_with_overhead;
+
+ open_env();
+ int r;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, db_page_size); CKERR(r);
+ r = db->set_readpagesize(db, db_basement_size); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ uint64_t *XMALLOC_N(nrows, keys);
+ for (uint64_t i = 0; i < nrows; i++)
+ keys[i] = 2*i + 1;
+
+ if (random_keys)
+ for (uint64_t i = 0; i < nrows; i++)
+ swap(keys, random() % nrows, random() % nrows);
+
+ // insert keys 1, 3, 5, ... 2*(nrows-1) + 1
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ if (use_loader) {
+ DB_LOADER *loader = NULL;
+ r = env->create_loader(env, txn, &loader, db, 1, &db, NULL, NULL, 0); CKERR(r);
+ for (uint64_t i=0; i<nrows; i++) {
+ char key[100],val[100];
+ snprintf(key, sizeof key, "%08llu", (unsigned long long)keys[i]);
+ snprintf(val, sizeof val, "%08llu", (unsigned long long)keys[i]);
+ assert(1+strlen(key) == key_size && 1+strlen(val) == val_size);
+ DBT k,v;
+ r = loader->put(loader, dbt_init(&k, key, 1+strlen(key)), dbt_init(&v,val, 1+strlen(val))); CKERR(r);
+ }
+ r = loader->close(loader); CKERR(r);
+ } else {
+ for (uint64_t i=0; i<nrows; i++) {
+ char key[100],val[100];
+ snprintf(key, sizeof key, "%08llu", (unsigned long long)keys[i]);
+ snprintf(val, sizeof val, "%08llu", (unsigned long long)keys[i]);
+ assert(1+strlen(key) == key_size && 1+strlen(val) == val_size);
+ DBT k,v;
+ r = db->put(db, txn, dbt_init(&k, key, 1+strlen(key)), dbt_init(&v,val, 1+strlen(val)), 0); CKERR(r);
+ }
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // close and reopen to get rid of basements
+ r = db->close(db, 0); CKERR(r); // close MUST flush the nodes of this db out of the cache table for this test to be valid
+ r = env->close(env, 0); CKERR(r);
+ env = NULL;
+ open_env();
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ if (get_all) {
+ // read the basements into memory
+ for (uint64_t i=0; i<nrows; i++) {
+ char key[100];
+ snprintf(key, 100, "%08llu", (unsigned long long)2*i+1);
+ DBT k,v;
+ memset(&v, 0, sizeof(v));
+ r = db->get(db, txn, dbt_init(&k, key, 1+strlen(key)), &v, 0); CKERR(r);
+ }
+ }
+
+ DB_BTREE_STAT64 s64;
+ r = db->stat64(db, txn, &s64); CKERR(r);
+ if (verbose)
+ printf("stats %" PRId64 " %" PRId64 "\n", s64.bt_nkeys, s64.bt_dsize);
+ if (use_loader) {
+ assert(s64.bt_nkeys == nrows);
+ assert(s64.bt_dsize == nrows * (key_size + val_size));
+ } else {
+ assert(0 < s64.bt_nkeys && s64.bt_nkeys <= nrows);
+ assert(0 < s64.bt_dsize && s64.bt_dsize <= nrows * (key_size + val_size));
+ }
+
+ if (0) goto skipit; // debug: just write the tree
+
+ bool last_basement;
+ last_basement = false;
+ // verify key_range for keys that exist in the tree
+ uint64_t random_fudge;
+ random_fudge = random_keys ? rows_per_basement + nrows / 10 : 0;
+ for (uint64_t i=0; i<nrows; i++) {
+ char key[100];
+ snprintf(key, 100, "%08llu", (unsigned long long)2*i+1);
+ DBT k;
+ uint64_t less,equal,greater;
+ int is_exact;
+ r = db->key_range64(db, txn, dbt_init(&k, key, 1+strlen(key)), &less, &equal, &greater, &is_exact); CKERR(r);
+ if (verbose)
+ printf("key %llu/%llu %llu %llu %llu %llu\n", (unsigned long long)2*i, (unsigned long long)2*nrows, (unsigned long long)less, (unsigned long long)equal, (unsigned long long)greater,
+ (unsigned long long)(less+equal+greater));
+ assert(is_exact == 0);
+ assert(0 < less + equal + greater);
+ if (use_loader) {
+ assert(less + equal + greater <= nrows);
+ if (get_all || last_basement) {
+ assert(equal == 1);
+ } else if (i < nrows - rows_per_basement * 2) {
+ assert(equal == 0);
+ } else if (i == nrows - 1) {
+ assert(equal == 1);
+ } else if (equal == 1) {
+ last_basement = true;
+ }
+ assert(less <= max64(i, i + rows_per_basement/2));
+ assert(greater <= nrows - less);
+ } else {
+ assert(less + equal + greater <= nrows + nrows / 8);
+ if (get_all || last_basement) {
+ assert(equal == 1);
+ } else if (i < nrows - rows_per_basement * 2) {
+ assert(equal == 0);
+ } else if (i == nrows - 1) {
+ assert(equal == 1);
+ } else if (equal == 1) {
+ last_basement = true;
+ }
+ uint64_t est_i = i * 2 + rows_per_basement;
+ assert(less <= est_i + random_fudge);
+ assert(greater <= nrows - i + rows_per_basement + random_fudge);
+ }
+ }
+
+ // verify key range for keys that do not exist in the tree
+ for (uint64_t i=0; i<1+nrows; i++) {
+ char key[100];
+ snprintf(key, 100, "%08llu", (unsigned long long)2*i);
+ DBT k;
+ uint64_t less,equal,greater;
+ int is_exact;
+ r = db->key_range64(db, txn, dbt_init(&k, key, 1+strlen(key)), &less, &equal, &greater, &is_exact); CKERR(r);
+ if (verbose)
+ printf("key %llu/%llu %llu %llu %llu %llu\n", (unsigned long long)2*i, (unsigned long long)2*nrows, (unsigned long long)less, (unsigned long long)equal, (unsigned long long)greater,
+ (unsigned long long)(less+equal+greater));
+ assert(is_exact == 0);
+ assert(0 < less + equal + greater);
+ if (use_loader) {
+ assert(less + equal + greater <= nrows);
+ assert(equal == 0);
+ assert(less <= max64(i, i + rows_per_basement/2));
+ assert(greater <= nrows - less);
+ } else {
+ assert(less + equal + greater <= nrows + nrows / 8);
+ assert(equal == 0);
+ uint64_t est_i = i * 2 + rows_per_basement;
+ assert(less <= est_i + random_fudge);
+ assert(greater <= nrows - i + rows_per_basement + random_fudge);
+ }
+ }
+
+ skipit:
+ r = txn->commit(txn, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ toku_free(keys);
+}
+
+static int
+usage(void) {
+ fprintf(stderr, "-v (verbose)\n");
+ fprintf(stderr, "-q (quiet)\n");
+ fprintf(stderr, "--nrows %" PRIu64 " (number of rows)\n", nrows);
+ fprintf(stderr, "--nrows %" PRIu64 " (number of rows)\n", nrows);
+ fprintf(stderr, "--loader %u (use the loader to load the keys)\n", use_loader);
+ fprintf(stderr, "--get %u (get all keys before keyrange)\n", get_all);
+ fprintf(stderr, "--random_keys %u\n", random_keys);
+ fprintf(stderr, "--page_size %u\n", db_page_size);
+ fprintf(stderr, "--basement_size %u\n", db_basement_size);
+ return 1;
+}
+
+int
+test_main (int argc , char * const argv[]) {
+ for (int i = 1 ; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--nrows") == 0 && i+1 < argc) {
+ nrows = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--get") == 0 && i+1 < argc) {
+ get_all = atoi(argv[++i]) != 0;
+ continue;
+ }
+ if (strcmp(argv[i], "--loader") == 0 && i+1 < argc) {
+ use_loader = atoi(argv[++i]) != 0;
+ continue;
+ }
+ if (strcmp(argv[i], "--random_keys") == 0 && i+1 < argc) {
+ random_keys = atoi(argv[++i]) != 0;
+ continue;
+ }
+ if (strcmp(argv[i], "--page_size") == 0 && i+1 < argc) {
+ db_page_size = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--basement_size") == 0 && i+1 < argc) {
+ db_basement_size = atoi(argv[++i]);
+ continue;
+ }
+ return usage();
+ }
+
+ toku_os_recursive_delete(envdir);
+ int r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ run_test();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/last-verify-time.cc b/storage/tokudb/PerconaFT/src/tests/last-verify-time.cc
new file mode 100644
index 00000000000..0c7fac4c1a4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/last-verify-time.cc
@@ -0,0 +1,150 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// test for the last verify time
+
+static void
+test_verify_time_after_create(DB_ENV *env) {
+ int r;
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+
+ r = db->open(db, NULL, "test.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_BTREE_STAT64 stats;
+ r = db->stat64(db, NULL, &stats); assert_zero(r);
+ assert(stats.bt_verify_time_sec == 0);
+
+ r = db->close(db, 0); assert_zero(r);
+}
+
+static void
+test_verify_time_after_open(DB_ENV *env) {
+ int r;
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+
+ r = db->open(db, NULL, "test.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_BTREE_STAT64 stats;
+ r = db->stat64(db, NULL, &stats); assert_zero(r);
+ assert(stats.bt_verify_time_sec == 0);
+
+ r = db->close(db, 0); assert_zero(r);
+}
+
+static void
+test_verify_time_after_check(DB_ENV *env) {
+ int r;
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+
+ r = db->open(db, NULL, "test.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_BTREE_STAT64 stats;
+ r = db->stat64(db, NULL, &stats); assert_zero(r);
+ assert(stats.bt_verify_time_sec == 0);
+
+ r = db->verify_with_progress(db, NULL, NULL, 0, 0); assert_zero(r);
+
+ r = db->stat64(db, NULL, &stats); assert_zero(r);
+ assert(stats.bt_verify_time_sec != 0);
+
+ r = db->close(db, 0); assert_zero(r);
+}
+
+static void
+test_verify_time_after_reopen(DB_ENV *env) {
+ int r;
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+
+ r = db->open(db, NULL, "test.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_BTREE_STAT64 stats;
+ r = db->stat64(db, NULL, &stats); assert_zero(r);
+ assert(stats.bt_verify_time_sec != 0);
+
+ r = db->close(db, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ assert(0);
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ test_verify_time_after_create(env);
+
+ test_verify_time_after_open(env);
+
+ test_verify_time_after_check(env);
+
+ test_verify_time_after_reopen(env);
+
+ r = env->close(env, 0); assert_zero(r);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-cleanup-test.cc b/storage/tokudb/PerconaFT/src/tests/loader-cleanup-test.cc
new file mode 100644
index 00000000000..ea894683c23
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-cleanup-test.cc
@@ -0,0 +1,1067 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* TODO:
+ *
+ * When ready, add simulated errors on calls to malloc()
+ *
+ */
+
+
+/* Purpose is to verify that when a loader fails:
+ * - there are no temp files remaining
+ * - the loader-generated iname file is not present
+ *
+ * A loader can fail in the following ways:
+ * - user calls loader->abort()
+ * - user aborts transaction
+ * - disk full (ENOSPC)
+ * - crash (not tested in this test program)
+ *
+ * Mechanism:
+ * This test is derived from the loader-stress-test.
+ *
+ * The outline of the test is as follows:
+ * - use loader to create table
+ * - verify presence of temp files
+ * - commit / abort / inject error (simulated error from system call)
+ * - verify absence of temp files
+ * - verify absence of unwanted iname files (old inames if committed, new inames if aborted)
+ *
+ *
+ */
+
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "ydb-internal.h"
+
+enum test_type {event, // any event
+ commit, // close loader, commit txn
+ abort_txn, // close loader, abort txn
+ abort_loader, // abort loader, abort txn
+ abort_via_poll, // close loader, but poll function returns non-zero, abort txn
+ enospc_w, // close loader, but close fails due to enospc return from toku_os_write
+ enospc_f, // either loader->put() or loader->close() fails due to enospc return from do_fwrite()
+ enospc_p, // loader->close() fails due to enospc return from toku_os_pwrite()
+ einval_fdo, // return einval from fdopen()
+ einval_fo, // return einval from fopen()
+ einval_o, // return einval from open()
+ enospc_fc}; // return enospc from fclose()
+
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+enum {MAX_DBS=256};
+#define default_NUM_DBS 5
+int NUM_DBS=default_NUM_DBS;
+#define default_NUM_ROWS 100000
+int NUM_ROWS=default_NUM_ROWS;
+//static int NUM_ROWS=50000000;
+int CHECK_RESULTS=0;
+int DISALLOW_PUTS=0;
+int COMPRESS=0;
+int event_trigger_lo=0; // what event triggers to use?
+int event_trigger_hi =0; // 0 and 0 mean none.
+enum {MAGIC=311};
+
+
+DBT old_inames[MAX_DBS];
+DBT new_inames[MAX_DBS];
+
+static const char *loader_temp_prefix = "tokuld"; // #2536
+static int count_temp(char * dirname);
+static void get_inames(DBT* inames, DB** dbs);
+static int verify_file(char * dirname, char * filename);
+static void assert_inames_missing(DBT* inames);
+static void run_all_tests(void);
+static void free_inames(DBT* inames);
+
+
+// how many different system calls are intercepted with error injection
+#define NUM_ERR_TYPES 7+1 // abort_via_poll does not exactly inject errors
+
+int64_t event_count = 0; // number of calls of all types so far (in this run)
+int64_t event_count_nominal = 0; // number of calls of all types in the nominally error-free run.
+int64_t event_count_trigger = 0; // which call will we complain about
+
+int fwrite_count = 0;
+int fwrite_count_nominal = 0; // number of fwrite calls for normal operation, initially zero
+int fwrite_count_trigger = 0; // sequence number of fwrite call that will fail (zero disables induced failure)
+
+int write_count = 0;
+int write_count_nominal = 0; // number of write calls for normal operation, initially zero
+int write_count_trigger = 0; // sequence number of write call that will fail (zero disables induced failure)
+
+int pwrite_count = 0;
+int pwrite_count_nominal = 0; // number of pwrite calls for normal operation, initially zero
+int pwrite_count_trigger = 0; // sequence number of pwrite call that will fail (zero disables induced failure)
+
+int fdopen_count = 0;
+int fdopen_count_nominal = 0; // number of fdopen calls for normal operation, initially zero
+int fdopen_count_trigger = 0; // sequence number of fdopen call that will fail (zero disables induced failure)
+
+int fopen_count = 0;
+int fopen_count_nominal = 0; // number of fopen calls for normal operation, initially zero
+int fopen_count_trigger = 0; // sequence number of fopen call that will fail (zero disables induced failure)
+
+int open_count = 0;
+int open_count_nominal = 0; // number of open calls for normal operation, initially zero
+int open_count_trigger = 0; // sequence number of open call that will fail (zero disables induced failure)
+
+int fclose_count = 0;
+int fclose_count_nominal = 0; // number of fclose calls for normal operation, initially zero
+int fclose_count_trigger = 0; // sequence number of fclose call that will fail (zero disables induced failure)
+
+int poll_count = 0;
+int poll_count_nominal = 0; // number of fclose calls for normal operation, initially zero
+int poll_count_trigger = 0; // sequence number of fclose call that will fail (zero disables induced failure)
+
+int error_injected = 0;
+
+static const char *
+err_type_str (enum test_type t) {
+ switch(t) {
+ case event: return "anyevent";
+ case enospc_f: return "fwrite";
+ case enospc_w: return "write";
+ case enospc_p: return "pwrite";
+ case einval_fdo: return "fdopen";
+ case einval_fo: return "fopen";
+ case einval_o: return "open";
+ case enospc_fc: return "fclose";
+ case abort_via_poll: return "abort_via_poll";
+ case commit: assert(0);
+ case abort_txn: assert(0);
+ case abort_loader: assert(0);
+ }
+ // I know that Barry prefers the single-return case, but writing the code this way means that the compiler will complain if I forget something in the enum. -Bradley
+ assert(0);
+ return NULL;
+}
+
+static const char *
+err_msg_type_str (enum test_type t) {
+ switch(t) {
+ case event: return "ENOSPC/EINVAL/POLL";
+ case enospc_f: return "ENOSPC";
+ case enospc_w: return "ENOSPC";
+ case enospc_p: return "ENOSPC";
+ case einval_fdo: return "EINVAL";
+ case einval_fo: return "EINVAL";
+ case einval_o: return "EINVAL";
+ case enospc_fc: return "ENOSPC";
+ case abort_via_poll: return "non-zero";
+ case commit: assert(0);
+ case abort_txn: assert(0);
+ case abort_loader: assert(0);
+ }
+ // I know that Barry prefers the single-return case, but writing the code this way means that the compiler will complain if I forget something in the enum. -Bradley
+ assert(0);
+ return NULL;
+}
+
+static size_t bad_fwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream) {
+ fwrite_count++;
+ event_count++;
+ size_t r;
+ if (fwrite_count_trigger == fwrite_count || event_count == event_count_trigger) {
+ error_injected++;
+ errno = ENOSPC;
+ r = (size_t) -1;
+ } else {
+ r = fwrite(ptr, size, nmemb, stream);
+ if (r!=nmemb) {
+ errno = ferror(stream);
+ }
+ }
+ return r;
+}
+
+
+static ssize_t
+bad_write(int fd, const void * bp, size_t len) {
+ ssize_t r;
+ write_count++;
+ event_count++;
+ if (write_count_trigger == write_count || event_count == event_count_trigger) {
+ error_injected++;
+ errno = ENOSPC;
+ r = -1;
+ } else {
+ r = write(fd, bp, len);
+ }
+ return r;
+}
+
+static ssize_t
+bad_pwrite (int fd, const void *buf, size_t len, toku_off_t off) {
+ int r;
+ pwrite_count++;
+ event_count++;
+ if (pwrite_count_trigger == pwrite_count || event_count == event_count_trigger) {
+ error_injected++;
+ errno = ENOSPC;
+ r = -1;
+ } else {
+ r = pwrite(fd, buf, len, off);
+ }
+ return r;
+}
+
+
+
+static FILE *
+bad_fdopen(int fd, const char * mode) {
+ FILE * rval;
+ fdopen_count++;
+ event_count++;
+ if (fdopen_count_trigger == fdopen_count || event_count == event_count_trigger) {
+ error_injected++;
+ errno = EINVAL;
+ rval = NULL;
+ } else {
+ rval = fdopen(fd, mode);
+ }
+ return rval;
+}
+
+static FILE *
+bad_fopen(const char *filename, const char *mode) {
+ FILE * rval;
+ fopen_count++;
+ event_count++;
+ if (fopen_count_trigger == fopen_count || event_count == event_count_trigger) {
+ error_injected++;
+ errno = EINVAL;
+ rval = NULL;
+ } else {
+ rval = fopen(filename, mode);
+ }
+ return rval;
+}
+
+
+static int
+bad_open(const char *path, int oflag, int mode) {
+ int rval;
+ open_count++;
+ event_count++;
+ if (open_count_trigger == open_count || event_count == event_count_trigger) {
+ error_injected++;
+ errno = EINVAL;
+ rval = -1;
+ } else {
+ rval = open(path, oflag, mode);
+ }
+ return rval;
+}
+
+
+
+static int
+bad_fclose(FILE * stream) {
+ int rval;
+ fclose_count++;
+ event_count++;
+ // Must close the stream even in the "error case" because otherwise there is no way to get the memory back.
+ rval = fclose(stream);
+ if (rval==0) {
+ if (fclose_count_trigger == fclose_count || event_count == event_count_trigger) {
+ error_injected++;
+ errno = ENOSPC;
+ rval = -1;
+ }
+ }
+ return rval;
+}
+
+
+
+///////////////
+
+
+// return number of temp files
+static int
+count_temp(char * dirname) {
+ int n = 0;
+
+ DIR * dir = opendir(dirname);
+
+ struct dirent *ent;
+ while ((ent=readdir(dir))) {
+ if ((ent->d_type==DT_REG || ent->d_type==DT_UNKNOWN) && strncmp(ent->d_name, loader_temp_prefix, 6)==0) {
+ n++;
+ if (verbose >= 3) {
+ printf("Temp files\n");
+ printf(" %s/%s\n", dirname, ent->d_name);
+ }
+ }
+ }
+ closedir(dir);
+ return n;
+}
+
+
+
+// return non-zero if file exists
+static int
+verify_file(char * dirname, char * filename) {
+ int n = 0;
+ DIR * dir = opendir(dirname);
+
+ struct dirent *ent;
+ while ((ent=readdir(dir))) {
+ if ((ent->d_type==DT_REG || ent->d_type==DT_UNKNOWN) && strcmp(ent->d_name, filename)==0) {
+ n++;
+ }
+ }
+ closedir(dir);
+ return n;
+}
+
+static void
+get_inames(DBT* inames, DB** dbs) {
+ int i;
+ for (i = 0; i < NUM_DBS; i++) {
+ DBT dname;
+ char * dname_str = dbs[i]->i->dname;
+ dbt_init(&dname, dname_str, strlen(dname_str)+1);
+ dbt_init(&(inames[i]), NULL, 0);
+ inames[i].flags |= DB_DBT_MALLOC;
+ int r = env->get_iname(env, &dname, &inames[i]);
+ CKERR(r);
+ char * iname_str = (char*) (inames[i].data);
+ if (verbose >= 2) printf("dname = %s, iname = %s\n", dname_str, iname_str);
+ }
+}
+
+
+static void
+assert_inames_missing(DBT* inames) {
+ int i;
+ char * dir = env->i->real_data_dir;
+ for (i=0; i<NUM_DBS; i++) {
+ char * CAST_FROM_VOIDP(iname, inames[i].data);
+ int r = verify_file(dir, iname);
+ if (r) {
+ printf("File %s exists, but it should not\n", iname);
+ }
+ assert(r == 0);
+ if (verbose) printf("File has been properly deleted: %s\n", iname);
+ }
+}
+
+static
+void free_inames(DBT* inames) {
+ int i;
+ for (i=0; i<NUM_DBS; i++) {
+ toku_free(inames[i].data);
+ }
+}
+
+#if 0
+static void
+print_inames(DB** dbs) {
+ int i;
+ for (i = 0; i < NUM_DBS; i++) {
+ DBT dname;
+ DBT iname;
+ char * dname_str = dbs[i]->i->dname;
+ dbt_init(&dname, dname_str, sizeof(dname_str));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ int r = env->get_iname(env, &dname, &iname);
+ CKERR(r);
+ char * iname_str = (char*)iname.data;
+ if (verbose) printf("dname = %s, iname = %s\n", dname_str, iname_str);
+ int n = verify_file(env->i->real_data_dir, iname_str);
+ assert(n == 1);
+ toku_free(iname.data);
+ }
+}
+#endif
+
+
+//
+// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
+//
+
+// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
+// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
+int a[MAX_DBS][32];
+int inv[MAX_DBS][32];
+
+
+// rotate right and left functions
+static inline unsigned int rotr32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x >> n) | ( x << (32 - n));
+}
+static inline unsigned int rotl32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x << n) | ( x >> (32 - n));
+}
+
+static void generate_permute_tables(void) {
+ int i, j, tmp;
+ for(int db=0;db<MAX_DBS;db++) {
+ for(i=0;i<32;i++) {
+ a[db][i] = i;
+ }
+ for(i=0;i<32;i++) {
+ j = random() % (i + 1);
+ tmp = a[db][j];
+ a[db][j] = a[db][i];
+ a[db][i] = tmp;
+ }
+// if(db < NUM_DBS){ printf("a[%d] = ", db); for(i=0;i<32;i++) { printf("%2d ", a[db][i]); } printf("\n");}
+ for(i=0;i<32;i++) {
+ inv[db][a[db][i]] = i;
+ }
+ }
+}
+
+// permute bits of x based on permute table bitmap
+static unsigned int twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << a[db][i];
+ }
+ return b;
+}
+
+// permute bits of x based on inverse permute table bitmap
+static unsigned int inv_twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << inv[db][i];
+ }
+ return b;
+}
+
+// generate val from key, index
+static unsigned int generate_val(int key, int i) {
+ return rotl32((key + MAGIC), i);
+}
+static unsigned int pkey_for_val(int key, int i) {
+ return rotr32(key, i) - MAGIC;
+}
+
+// There is no handlerton in this test, so this function is a local replacement
+// for the handlerton's generate_row_for_put().
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ (void) src_db;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ if ( which == 0 ) {
+ if (dest_key->flags==DB_DBT_REALLOC) {
+ if (dest_key->data) toku_free(dest_key->data);
+ dest_key->flags = 0;
+ dest_key->ulen = 0;
+ }
+ if (dest_val->flags==DB_DBT_REALLOC) {
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_val->flags = 0;
+ dest_val->ulen = 0;
+ }
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+ }
+ else {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(unsigned int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(unsigned int));
+ dest_key->ulen = sizeof(unsigned int);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < sizeof(unsigned int)) {
+ dest_val->data = toku_xrealloc(dest_val->data, sizeof(unsigned int));
+ dest_val->ulen = sizeof(unsigned int);
+ }
+ unsigned int *new_key = (unsigned int *)dest_key->data;
+ unsigned int *new_val = (unsigned int *)dest_val->data;
+
+ *new_key = twiddle32(*(unsigned int*)src_key->data, which);
+ *new_val = generate_val(*(unsigned int*)src_key->data, which);
+
+ dest_key->size = sizeof(unsigned int);
+ dest_val->size = sizeof(unsigned int);
+ //data is already set above
+ }
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+
+static void check_results(DB **dbs)
+{
+ for(int j=0;j<NUM_DBS;j++){
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ int r;
+ unsigned int pkey_for_db_key;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+ for(int i=0;i<NUM_ROWS;i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, EINVAL);
+ } else {
+ CKERR(r);
+ k = *(unsigned int*)key.data;
+ pkey_for_db_key = (j == 0) ? k : inv_twiddle32(k, j);
+ v = *(unsigned int*)val.data;
+ // test that we have the expected keys and values
+ assert((unsigned int)pkey_for_db_key == (unsigned int)pkey_for_val(v, j));
+// printf(" DB[%d] key = %10u, val = %10u, pkey_for_db_key = %10u, pkey_for_val=%10d\n", j, v, k, pkey_for_db_key, pkey_for_val(v, j));
+ }
+ }
+ {printf("."); fflush(stdout);}
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ printf("\nCheck OK\n");
+}
+
+static void *expect_poll_void = &expect_poll_void;
+static int poll_function (void *extra, float progress) {
+ int r;
+ if (0) {
+ static int did_one=0;
+ static struct timeval start;
+ struct timeval now;
+ gettimeofday(&now, 0);
+ if (!did_one) {
+ start=now;
+ did_one=1;
+ }
+ printf("%6.6f %5.1f%%\n", now.tv_sec - start.tv_sec + 1e-6*(now.tv_usec - start.tv_usec), progress*100);
+ }
+ assert(extra==expect_poll_void);
+ assert(0.0<=progress && progress<=1.0);
+ poll_count++;
+ event_count++;
+ if (poll_count_trigger == poll_count || event_count == event_count_trigger) {
+ r = 1;
+ }
+ else {
+ r = 0;
+ }
+ return r;
+}
+
+static void test_loader(enum test_type t, DB **dbs, int trigger)
+{
+ int failed_put = 0;
+ int error_injection; // are we expecting simulated errors from system calls?
+ error_injected = 0; // number of errors actually injected
+
+ if (t == commit ||
+ t == abort_txn ||
+ t == abort_loader ||
+ t == abort_via_poll)
+ error_injection = 0;
+ else
+ error_injection = 1;
+
+
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[MAX_DBS];
+ uint32_t dbt_flags[MAX_DBS];
+ for(int i=0;i<MAX_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = DISALLOW_PUTS | COMPRESS; // set with -p/-z option
+
+ if (verbose >= 2)
+ printf("old inames:\n");
+ get_inames(old_inames, dbs);
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ r = loader->set_error_callback(loader, NULL, NULL);
+ CKERR(r);
+ r = loader->set_poll_function(loader, poll_function, expect_poll_void);
+ CKERR(r);
+
+ if (verbose) {
+ printf("DISALLOW_PUTS = %d\n", DISALLOW_PUTS);
+ printf("COMPRESS = %d\n", COMPRESS);
+ }
+ if (verbose >= 2)
+ printf("new inames:\n");
+ get_inames(new_inames, dbs);
+
+ // using loader->put, put values into DB
+ DBT key, val;
+ unsigned int k, v;
+ for(int i=1;i<=NUM_ROWS && !failed_put;i++) {
+ k = i;
+ v = generate_val(i, 0);
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ if (DISALLOW_PUTS) {
+ assert(r == EINVAL);
+ } else if (r != 0) {
+ assert(error_injection && error_injected);
+ failed_put = r;
+ }
+ if ( CHECK_RESULTS || verbose) { if((i%10000) == 0){printf("."); fflush(stdout);} }
+ }
+ if( CHECK_RESULTS || verbose ) {printf("\n"); fflush(stdout);}
+
+ assert(poll_count == 0); // no polling before loader->close() is called
+
+ // You cannot count the temp files here.
+ if (verbose) {
+ printf("Data dir is %s\n", env->i->real_data_dir);
+ }
+ if (t == commit || t == abort_txn) {
+ // close the loader
+ if (verbose) {
+ printf("closing\n");
+ fflush(stdout);
+ }
+ r = loader->close(loader);
+ CKERR(r);
+ if (!DISALLOW_PUTS) {
+ assert(poll_count>0);
+ // You cannot count temp files here
+ }
+ }
+ else if (t == abort_via_poll) {
+ assert(!DISALLOW_PUTS); // test makes no sense with DISALLOW_PUTS
+ if (verbose)
+ printf("closing, but expecting abort via poll\n");
+ r = loader->close(loader);
+ if (r == 0) {
+ printf("loader->close() returned 0 but should have failed due to non-zero return from polling function.\n");
+ fflush(stdout);
+ }
+ assert(r); // not defined what close() returns when poll function returns non-zero
+ }
+ else if (error_injection && !failed_put) {
+ const char * type = err_type_str(t);
+ r = loader->close(loader);
+ if (verbose) {
+ if (error_injected)
+ printf("closing, but expecting failure from simulated error (enospc or einval)%s\n", type);
+ else
+ printf("closing, expecting no error because number of system calls was less than predicted (%s)\n", type);
+ }
+ if (!DISALLOW_PUTS && error_injected) {
+ if (r == 0) {
+ printf("loader->close() returned 0 but should have failed due to injected error from %s on call %d\n",
+ err_type_str(t), trigger);
+ fflush(stdout);
+ }
+ assert(r);
+ }
+ else
+ CKERR(r); // if using puts, "outer" loader should close without error, if no errors injected should also close without error
+ }
+ else {
+ if (verbose)
+ printf("aborting loader");
+ r = loader->abort(loader);
+ CKERR(r);
+ }
+ int n = count_temp(env->i->real_data_dir);
+ if (verbose) printf("Num temp files = %d\n", n);
+ fflush(stdout);
+ assert(n==0);
+
+ if (verbose)
+ printf(" done\n");
+
+ if (t == commit) {
+ event_count_nominal = event_count;
+ fwrite_count_nominal = fwrite_count; // capture how many fwrites were required for normal operation
+ write_count_nominal = write_count; // capture how many writes were required for normal operation
+ pwrite_count_nominal = pwrite_count; // capture how many pwrites were required for normal operation
+ fdopen_count_nominal = fdopen_count; // capture how many fdopens were required for normal operation
+ fopen_count_nominal = fopen_count; // capture how many fopens were required for normal operation
+ open_count_nominal = open_count; // capture how many opens were required for normal operation
+ fclose_count_nominal = fclose_count; // capture how many fcloses were required for normal operation
+ poll_count_nominal = poll_count; // capture how many times the polling function was called
+
+ if (verbose) {
+ printf("Nominal calls: function calls (number of calls for normal operation)\n");
+ printf(" events %" PRId64 "\n", event_count_nominal);
+ printf(" fwrite %d\n", fwrite_count_nominal);
+ printf(" write %d\n", write_count_nominal);
+ printf(" pwrite %d\n", pwrite_count_nominal);
+ printf(" fdopen %d\n", fdopen_count_nominal);
+ printf(" fopen %d\n", fopen_count_nominal);
+ printf(" open %d\n", open_count_nominal);
+ printf(" fclose %d\n", fclose_count_nominal);
+ printf(" poll %d\n", poll_count_nominal);
+ }
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ if (!DISALLOW_PUTS) {
+ assert_inames_missing(old_inames);
+ }
+ if ( CHECK_RESULTS ) {
+ check_results(dbs);
+ }
+
+ }
+ else {
+ r = txn->abort(txn);
+ CKERR(r);
+ if (!DISALLOW_PUTS) {
+ assert_inames_missing(new_inames);
+ }
+ }
+ free_inames(old_inames);
+ free_inames(new_inames);
+}
+
+
+static int run_test_count = 0;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static void run_test(enum test_type t, int trigger)
+{
+ run_test_count++;
+
+ int r;
+
+ if (verbose>0) { // Don't print anything if verbose is 0. Use "+" to indicate progress if verbose is positive
+ printf("+");
+ fflush(stdout);
+ }
+
+ toku_os_recursive_delete(envdir);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ generate_permute_tables();
+
+ event_count_trigger = event_count = 0;
+ fwrite_count_trigger = fwrite_count = 0;
+ write_count_trigger = write_count = 0;
+ pwrite_count_trigger = pwrite_count = 0;
+ fdopen_count_trigger = fdopen_count = 0;
+ fopen_count_trigger = fopen_count = 0;
+ open_count_trigger = open_count = 0;
+ fclose_count_trigger = fclose_count = 0;
+ poll_count_trigger = poll_count = 0;
+
+ switch(t) {
+ case commit:
+ case abort_txn:
+ case abort_loader:
+ break;
+ case event:
+ event_count_trigger = trigger; break;
+ case enospc_f:
+ fwrite_count_trigger = trigger; break;
+ case enospc_w:
+ write_count_trigger = trigger; break;
+ case enospc_p:
+ pwrite_count_trigger = trigger; break;
+ case einval_fdo:
+ fdopen_count_trigger = trigger; break;
+ case einval_fo:
+ fopen_count_trigger = trigger; break;
+ case einval_o:
+ open_count_trigger = trigger; break;
+ case enospc_fc:
+ fclose_count_trigger = trigger; break;
+ case abort_via_poll:
+ poll_count_trigger = trigger; break;
+ default:
+ assert(0);
+ }
+
+
+ db_env_set_func_loader_fwrite(bad_fwrite);
+ db_env_set_func_write(bad_write);
+ db_env_set_func_pwrite(bad_pwrite);
+ db_env_set_func_fdopen(bad_fdopen);
+ db_env_set_func_fopen(bad_fopen);
+ db_env_set_func_open(bad_open);
+ db_env_set_func_fclose(bad_fclose);
+
+ test_loader(t, dbs, trigger);
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ if (verbose >= 3)
+ print_engine_status(env);
+
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+static void run_all_tests(void) {
+ int trigger;
+
+ if (verbose) printf("\n\nTesting loader with loader close and txn commit (normal)\n");
+ run_test(commit, 0);
+
+ if (verbose) printf("\n\nTesting loader with loader abort and txn abort\n");
+ run_test(abort_loader, 0);
+
+ if (verbose) printf("\n\nTesting loader with loader close and txn abort\n");
+ run_test(abort_txn, 0);
+
+ if (event_trigger_lo || event_trigger_hi) {
+ printf("\n\nDoing events %d-%d\n", event_trigger_lo, event_trigger_hi);
+ for (int i=event_trigger_lo; i<=event_trigger_hi; i++) {
+ run_test(event, i);
+ }
+ } else {
+
+ enum test_type et[NUM_ERR_TYPES] = {enospc_f, enospc_w, enospc_p, einval_fdo, einval_fo, einval_o, enospc_fc, abort_via_poll};
+ int * nomp[NUM_ERR_TYPES] = {&fwrite_count_nominal, &write_count_nominal, &pwrite_count_nominal,
+ &fdopen_count_nominal, &fopen_count_nominal, &open_count_nominal,
+ &fclose_count_nominal, &poll_count_nominal};
+ int limit = NUM_DBS * 5;
+ int j;
+ for (j = 0; j<NUM_ERR_TYPES; j++) {
+ enum test_type t = et[j];
+ const char * err_type = err_type_str(t);
+ const char * err_msg_type = err_msg_type_str(t);
+
+ int nominal = *(nomp[j]);
+ if (verbose)
+ printf("\nNow test with induced %s returned from %s, nominal = %d\n", err_msg_type, err_type, nominal);
+ int i;
+ // induce write error at beginning of process
+ for (i = 1; i < limit && i < nominal+1; i++) {
+ trigger = i;
+ if (verbose) printf("\n\nTesting loader with %s induced at %s count %d (of %d)\n",
+ err_msg_type, err_type, trigger, nominal);
+ run_test(t, trigger);
+ }
+ if (nominal > limit) { // if we didn't already test every possible case
+ // induce write error sprinkled through process
+ for (i = 2; i < 5; i++) {
+ trigger = nominal / i;
+ if (verbose) printf("\n\nTesting loader with %s induced at %s count %d (of %d)\n",
+ err_msg_type, err_type, trigger, nominal);
+ run_test(t, trigger);
+ }
+ // induce write error at end of process
+ for (i = 0; i < limit; i++) {
+ trigger = nominal - i;
+ assert(trigger > 0);
+ if (verbose) printf("\n\nTesting loader with %s induced at %s count %d (of %d)\n",
+ err_msg_type, err_type, trigger, nominal);
+ run_test(t, trigger);
+ }
+ }
+ }
+ }
+}
+
+static int test_only_abort_via_poll = 0;
+
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ if (test_only_abort_via_poll) {
+ printf("Testing only normal operation and abort via polling, but test abort_via_polling exhaustively.\n");
+ if (verbose) {
+ print_time_now();
+ printf(": Testing loader with loader close and txn commit (normal)\n");
+ }
+ run_test(commit, 0);
+ if (verbose) {
+ printf("\n\nTesting loader with abort_via_polling exhaustively,\n");
+ printf("returning 1 from polling function on each iteration from 1 to %d\n", poll_count_nominal);
+ }
+ for (int i = 1; i < poll_count_nominal+1; i++) {
+ const char * err_type = err_type_str(abort_via_poll);
+ const char * err_msg_type = err_msg_type_str(abort_via_poll);
+ if (verbose) {
+ print_time_now();
+ printf(": Testing loader with %s induced at %s count %d (of %d)\n",
+ err_msg_type, err_type, i, poll_count_nominal);
+ print_time_now();
+ }
+ run_test(abort_via_poll, i);
+ }
+ if (verbose) {
+ print_time_now();
+ printf(": Done.\n");
+ }
+ }
+ else
+ run_all_tests();
+ printf("run_test_count=%d\n", run_test_count);
+ return 0;
+}
+
+static void usage(const char *cmd) {
+ fprintf(stderr, "Usage: -h -c -s -p -d <num_dbs> -r <num_rows> -t <elow> <ehi> \n%s\n", cmd);
+ fprintf(stderr, " where -h print this message.\n");
+ fprintf(stderr, " -c check the results.\n");
+ fprintf(stderr, " -p LOADER_DISALLOW_PUTS.\n");
+ fprintf(stderr, " -z LOADER_COMPRESS_INTERMEDIATES.\n");
+ fprintf(stderr, " -k Test only normal operation and abort_via_poll (but thoroughly).\n");
+ fprintf(stderr, " -s size_factor=1.\n");
+ fprintf(stderr, " -d <num_dbs> Number of indexes to create (default=%d).\n", default_NUM_DBS);
+ fprintf(stderr, " -r <num_rows> Number of rows to put (default=%d).\n", default_NUM_ROWS);
+ fprintf(stderr, " -t <elo> <ehi> Instrument only events <elo> to <ehi> (default: instrument all).\n");
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ usage(cmd); exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0 && argc > 1) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-r")==0 && argc > 1) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-c")==0) {
+ CHECK_RESULTS = 1;
+ } else if (strcmp(argv[0], "-z")==0) {
+ COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-p")==0) {
+ DISALLOW_PUTS = LOADER_DISALLOW_PUTS;
+ printf("DISABLED Using puts as part of #4503\n");
+ } else if (strcmp(argv[0], "-k")==0) {
+ test_only_abort_via_poll = 1;
+ printf("Perform only abort_via_poll test\n");
+ } else if (strcmp(argv[0], "-t")==0 && argc > 2) {
+ argc--; argv++;
+ event_trigger_lo = atoi(argv[0]);
+ argc--; argv++;
+ event_trigger_hi = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-s")==0) {
+ db_env_set_loader_size_factor(1);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-close-nproc-limit.cc b/storage/tokudb/PerconaFT/src/tests/loader-close-nproc-limit.cc
new file mode 100644
index 00000000000..f8b16126ecb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-close-nproc-limit.cc
@@ -0,0 +1,143 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/resource.h>
+
+static int loader_flags = 0;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static void run_test(int ndb) {
+ int r;
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+
+ DB *dbs[ndb];
+ uint32_t db_flags[ndb];
+ uint32_t dbt_flags[ndb];
+ for (int i = 0; i < ndb; i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char name[32];
+ sprintf(name, "db%d", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ }
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DB_LOADER *loader;
+ r = env->create_loader(env, txn, &loader, ndb > 0 ? dbs[0] : NULL, ndb, dbs, db_flags, dbt_flags, loader_flags); CKERR(r);
+
+ struct rlimit current_nproc_limit;
+ r = getrlimit(RLIMIT_NPROC, &current_nproc_limit);
+ assert(r == 0);
+
+ struct rlimit new_nproc_limit = current_nproc_limit;
+ new_nproc_limit.rlim_cur = 0;
+ r = setrlimit(RLIMIT_NPROC, &new_nproc_limit);
+ assert(r == 0);
+
+ r = loader->close(loader);
+
+ if (loader_flags & LOADER_DISALLOW_PUTS)
+ CKERR(r);
+ else
+ CKERR2(r, EAGAIN);
+
+ r = setrlimit(RLIMIT_NPROC, &current_nproc_limit);
+ assert(r == 0);
+
+ r = txn->abort(txn); CKERR(r);
+
+ for (int i = 0; i < ndb; i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: %s -h -v -q -p\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-p") == 0) {
+ loader_flags |= LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-z") == 0) {
+ loader_flags |= LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-e") == 0) {
+ argc--; argv++;
+ if (argc > 0)
+ envdir = argv[0];
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test(1);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-create-abort.cc b/storage/tokudb/PerconaFT/src/tests/loader-create-abort.cc
new file mode 100644
index 00000000000..ca0d5019f3d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-create-abort.cc
@@ -0,0 +1,118 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Ensure that loader->abort free all of its resources. The test just creates a loader and then
+// aborts it.
+
+#include "test.h"
+#include <db.h>
+
+static int loader_flags = 0;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static int put_multiple_generate(DB *UU(dest_db), DB *UU(src_db), DBT_ARRAY *UU(dest_keys), DBT_ARRAY *UU(dest_vals), const DBT *UU(src_key), const DBT *UU(src_val)) {
+ return ENOMEM;
+}
+
+static void loader_open_abort(void) {
+ int r;
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DB_LOADER *loader;
+ r = env->create_loader(env, txn, &loader, NULL, 0, NULL, NULL, NULL, loader_flags); CKERR(r);
+
+ r = loader->abort(loader); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: [-h] [-v] [-q] [-p]\n%s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-p") == 0) {
+ loader_flags |= LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-z") == 0) {
+ loader_flags |= LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-e") == 0) {
+ argc--; argv++;
+ if (argc > 0)
+ envdir = argv[0];
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ loader_open_abort();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-create-close.cc b/storage/tokudb/PerconaFT/src/tests/loader-create-close.cc
new file mode 100644
index 00000000000..eb9b3f26db0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-create-close.cc
@@ -0,0 +1,130 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Ensure that loader->abort free all of its resources. The test just creates a loader and then
+// aborts it.
+
+#include "test.h"
+#include <db.h>
+
+static int loader_flags = 0;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static void test_loader_create_close(int ndb) {
+ int r;
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+
+ DB *dbs[ndb];
+ uint32_t db_flags[ndb];
+ uint32_t dbt_flags[ndb];
+ for (int i = 0; i < ndb; i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char name[32];
+ sprintf(name, "db%d", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ }
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DB_LOADER *loader;
+ r = env->create_loader(env, txn, &loader, ndb > 0 ? dbs[0] : NULL, ndb, dbs, db_flags, dbt_flags, loader_flags); CKERR(r);
+
+ r = loader->close(loader); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ for (int i = 0; i < ndb; i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: %s -h -v -q -p\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-p") == 0) {
+ loader_flags |= LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-z") == 0) {
+ loader_flags |= LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-e") == 0) {
+ argc--; argv++;
+ if (argc > 0)
+ envdir = argv[0];
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ test_loader_create_close(0);
+ test_loader_create_close(1);
+ test_loader_create_close(2);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-create-commit-nproc-limit.cc b/storage/tokudb/PerconaFT/src/tests/loader-create-commit-nproc-limit.cc
new file mode 100644
index 00000000000..e158a00d4e0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-create-commit-nproc-limit.cc
@@ -0,0 +1,159 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// This test crashes if a failed loader creation causes the db to be corrupted by unlinking
+// the underlying fractal tree files. This unlinking occurs because the txn that logs the
+// load log entries is committed rather than aborted.
+
+#include "test.h"
+#include <db.h>
+#include <sys/resource.h>
+
+static int loader_flags = 0;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static void run_test(int ndb) {
+ int r;
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+
+ DB *dbs[ndb];
+ uint32_t db_flags[ndb];
+ uint32_t dbt_flags[ndb];
+ for (int i = 0; i < ndb; i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char name[32];
+ sprintf(name, "db%d", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ }
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ struct rlimit current_nproc_limit;
+ r = getrlimit(RLIMIT_NPROC, &current_nproc_limit);
+ assert(r == 0);
+
+ struct rlimit new_nproc_limit = current_nproc_limit;
+ new_nproc_limit.rlim_cur = 0;
+ r = setrlimit(RLIMIT_NPROC, &new_nproc_limit);
+ assert(r == 0);
+
+ DB_LOADER *loader;
+ int loader_r = env->create_loader(env, txn, &loader, ndb > 0 ? dbs[0] : NULL, ndb, dbs, db_flags, dbt_flags, loader_flags);
+
+ r = setrlimit(RLIMIT_NPROC, &current_nproc_limit);
+ assert(r == 0);
+
+ if (loader_flags & LOADER_DISALLOW_PUTS) {
+ CKERR(loader_r);
+ loader_r = loader->close(loader);
+ CKERR(loader_r);
+ } else {
+ CKERR2(loader_r, EAGAIN);
+ }
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ for (int i = 0; i < ndb; i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ for (int i = 0; i < ndb; i++) {
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char name[32];
+ sprintf(name, "db%d", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, 0, 0666); CKERR(r);
+ }
+
+ for (int i = 0; i < ndb; i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: %s -h -v -q -p\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-p") == 0) {
+ loader_flags |= LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-z") == 0) {
+ loader_flags |= LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-e") == 0) {
+ argc--; argv++;
+ if (argc > 0)
+ envdir = argv[0];
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test(1);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-create-nproc-limit.cc b/storage/tokudb/PerconaFT/src/tests/loader-create-nproc-limit.cc
new file mode 100644
index 00000000000..6d4b4d7b5df
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-create-nproc-limit.cc
@@ -0,0 +1,147 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Verify that env->create_loader works correctly (does not crash, does not leak memory, returns the right error code)
+// when the NPROC limit is exceeded.
+
+#include "test.h"
+#include <db.h>
+#include <sys/resource.h>
+
+static int loader_flags = 0;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static void run_test(int ndb) {
+ int r;
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+
+ DB *dbs[ndb];
+ uint32_t db_flags[ndb];
+ uint32_t dbt_flags[ndb];
+ for (int i = 0; i < ndb; i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ char name[32];
+ sprintf(name, "db%d", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ }
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ struct rlimit current_nproc_limit;
+ r = getrlimit(RLIMIT_NPROC, &current_nproc_limit);
+ assert(r == 0);
+
+ struct rlimit new_nproc_limit = current_nproc_limit;
+ new_nproc_limit.rlim_cur = 0;
+ r = setrlimit(RLIMIT_NPROC, &new_nproc_limit);
+ assert(r == 0);
+
+ DB_LOADER *loader;
+ int loader_r = env->create_loader(env, txn, &loader, ndb > 0 ? dbs[0] : NULL, ndb, dbs, db_flags, dbt_flags, loader_flags);
+
+ r = setrlimit(RLIMIT_NPROC, &current_nproc_limit);
+ assert(r == 0);
+
+ if (loader_flags & LOADER_DISALLOW_PUTS) {
+ CKERR(loader_r);
+ loader_r = loader->close(loader);
+ CKERR(loader_r);
+ } else {
+ CKERR2(loader_r, EAGAIN);
+ }
+
+ r = txn->abort(txn); CKERR(r);
+
+ for (int i = 0; i < ndb; i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: %s -h -v -q -p\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-p") == 0) {
+ loader_flags |= LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-z") == 0) {
+ loader_flags |= LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-e") == 0) {
+ argc--; argv++;
+ if (argc > 0)
+ envdir = argv[0];
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test(1);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-dup-test.cc b/storage/tokudb/PerconaFT/src/tests/loader-dup-test.cc
new file mode 100644
index 00000000000..3f2f8d7455a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-dup-test.cc
@@ -0,0 +1,452 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+enum {MAX_DBS=256};
+int NUM_DBS=5;
+int NUM_ROWS=100000;
+int CHECK_RESULTS=0;
+int DISALLOW_PUTS=0;
+int COMPRESS=0;
+enum {MAGIC=311};
+
+bool dup_row_at_end = false; // false: duplicate at the begining. true: duplicate at the end. The duplicated row is row 0.
+int dup_row_id = 0; // 0 means to use row 1 if inserting at the end, row NUM_ROWS if inserting at the beginning. Otherwise insert the row specified here.
+
+//
+// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
+//
+
+// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
+// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
+int a[MAX_DBS][32];
+int inv[MAX_DBS][32];
+
+
+// rotate right and left functions
+static inline unsigned int rotr32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x >> n) | ( x << (32 - n));
+}
+static inline unsigned int rotl32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x << n) | ( x >> (32 - n));
+}
+
+static void generate_permute_tables(void) {
+ int i, j, tmp;
+ for(int db=0;db<MAX_DBS;db++) {
+ for(i=0;i<32;i++) {
+ a[db][i] = i;
+ }
+ for(i=0;i<32;i++) {
+ j = random() % (i + 1);
+ tmp = a[db][j];
+ a[db][j] = a[db][i];
+ a[db][i] = tmp;
+ }
+// if(db < NUM_DBS){ printf("a[%d] = ", db); for(i=0;i<32;i++) { printf("%2d ", a[db][i]); } printf("\n");}
+ for(i=0;i<32;i++) {
+ inv[db][a[db][i]] = i;
+ }
+ }
+}
+
+// permute bits of x based on permute table bitmap
+static unsigned int twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << a[db][i];
+ }
+ return b;
+}
+
+// permute bits of x based on inverse permute table bitmap
+static unsigned int inv_twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << inv[db][i];
+ }
+ return b;
+}
+
+// generate val from key, index
+static unsigned int generate_val(int key, int i) {
+ return rotl32((key + MAGIC), i);
+}
+static unsigned int pkey_for_val(int key, int i) {
+ return rotr32(key, i) - MAGIC;
+}
+
+// There is no handlerton in this test, so this function is a local replacement
+// for the handlerton's generate_row_for_put().
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ (void) src_db;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ if ( which == 0 ) {
+ if (dest_key->flags==DB_DBT_REALLOC) {
+ if (dest_key->data) toku_free(dest_key->data);
+ dest_key->flags = 0;
+ dest_key->ulen = 0;
+ }
+ if (dest_val->flags==DB_DBT_REALLOC) {
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_val->flags = 0;
+ dest_val->ulen = 0;
+ }
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+ }
+ else {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(unsigned int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(unsigned int));
+ dest_key->ulen = sizeof(unsigned int);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < sizeof(unsigned int)) {
+ dest_val->data = toku_xrealloc(dest_val->data, sizeof(unsigned int));
+ dest_val->ulen = sizeof(unsigned int);
+ }
+ unsigned int *new_key = (unsigned int *)dest_key->data;
+ unsigned int *new_val = (unsigned int *)dest_val->data;
+
+ *new_key = twiddle32(*(unsigned int*)src_key->data, which);
+ *new_val = generate_val(*(unsigned int*)src_key->data, which);
+
+ dest_key->size = sizeof(unsigned int);
+ dest_val->size = sizeof(unsigned int);
+ //data is already set above
+ }
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+
+static void check_results(DB **dbs)
+{
+ for(int j=0;j<NUM_DBS;j++){
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ int r;
+ unsigned int pkey_for_db_key;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+ for(int i=0;i<NUM_ROWS;i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, EINVAL);
+ } else {
+ CKERR(r);
+ k = *(unsigned int*)key.data;
+ pkey_for_db_key = (j == 0) ? k : inv_twiddle32(k, j);
+ v = *(unsigned int*)val.data;
+ // test that we have the expected keys and values
+ assert((unsigned int)pkey_for_db_key == (unsigned int)pkey_for_val(v, j));
+// printf(" DB[%d] key = %10u, val = %10u, pkey_for_db_key = %10u, pkey_for_val=%10d\n", j, v, k, pkey_for_db_key, pkey_for_val(v, j));
+ }
+ }
+ {printf("."); fflush(stdout);}
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ printf("\nCheck OK\n");
+}
+
+struct error_extra {
+ int bad_i;
+ int error_count;
+};
+
+static void error_callback (DB *db, int which_db, int err, DBT *key, DBT *val, void *extra) {
+ assert(db);
+ assert(extra);
+ assert(err==DB_KEYEXIST);
+ assert(which_db>=0);
+ assert(key->size==4);
+ assert(which_db==0);
+ struct error_extra *e =(struct error_extra *)extra;
+ assert(e->bad_i == *(int*)key->data);
+ (void)val;
+ assert(e->error_count==0);
+ e->error_count++;
+}
+
+static void test_loader(DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[MAX_DBS];
+ uint32_t dbt_flags[MAX_DBS];
+ for(int i=0;i<MAX_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = DISALLOW_PUTS | COMPRESS; // set with -p option
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ struct error_extra error_extra = {.bad_i = 0, .error_count=0};
+ r = loader->set_error_callback(loader, error_callback, (void*)&error_extra);
+ CKERR(r);
+ r = loader->set_poll_function(loader, NULL, NULL);
+ CKERR(r);
+
+ // using loader->put, put values into DB
+ DBT key, val;
+ unsigned int k, v;
+ if (!dup_row_at_end) {
+ // put a duplicate row in.
+ int i = dup_row_id==0 ? NUM_ROWS : dup_row_id;
+ k = i;
+ v = generate_val(i, 0);
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ CKERR(r);
+ if ( CHECK_RESULTS || verbose) { if((i%10000) == 0){printf("."); fflush(stdout);} }
+ error_extra.bad_i = i;
+ }
+ for(int i=1;i<=NUM_ROWS;i++) {
+ k = i;
+ v = generate_val(i, 0);
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, EINVAL);
+ } else {
+ CKERR(r);
+ }
+ if ( CHECK_RESULTS || verbose) { if((i%10000) == 0){printf("."); fflush(stdout);} }
+ }
+ if (dup_row_at_end) {
+ // put a duplicate row in.
+ int i = dup_row_id==0 ? 1 : dup_row_id;
+ k = i;
+ v = generate_val(i, 0);
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ CKERR(r);
+ if ( CHECK_RESULTS || verbose) { if((i%10000) == 0){printf("."); fflush(stdout);} }
+ error_extra.bad_i = i;
+ }
+
+ if( CHECK_RESULTS || verbose ) {printf("\n"); fflush(stdout);}
+
+ // close the loader
+ if (verbose) { printf("closing"); fflush(stdout); }
+ r = loader->close(loader);
+ if (verbose) { printf(" done\n"); }
+ if (NUM_ROWS > 0) {
+ assert(r==DB_KEYEXIST);
+ assert(error_extra.error_count==1);
+ }
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // verify the DBs
+ if ( CHECK_RESULTS ) {
+ check_results(dbs);
+ }
+}
+
+char *free_me = NULL;
+const char *env_dir = TOKU_TEST_FILENAME; // the default env_dir
+
+static void run_test(void)
+{
+ int r;
+ toku_os_recursive_delete(env_dir);
+ r = toku_os_mkdir(env_dir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOG | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ generate_permute_tables();
+
+ if (verbose) printf("running test_loader()\n");
+ // -------------------------- //
+ test_loader(dbs);
+ // -------------------------- //
+ if (verbose) printf("done test_loader()\n");
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int num_rows_set = false;
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ if (num_rows_set)
+ run_test();
+ else {
+ int sizes[]={1,4000000,-1};
+ //Make PUT loader take about the same amount of time:
+ if (DISALLOW_PUTS) sizes[1] /= 25;
+ for (int i=0; sizes[i]>=0; i++) {
+ if (verbose) printf("Doing %d\n", sizes[i]);
+ NUM_ROWS = sizes[i];
+ run_test();
+ }
+ }
+ if (free_me) toku_free(free_me);
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: %s -h -c -d %d -r %d\n", cmd, NUM_DBS, NUM_ROWS);
+ fprintf(stderr, " where -e <env> uses <env> to construct the directory (so that different tests can run concurrently)\n");
+ fprintf(stderr, " -s use size factor of 1 (makes internal loader buffers small so certain cases are easier to test)\n");
+ fprintf(stderr, " -E duplicate the first row at the end (not the beginning).\n");
+ fprintf(stderr, " -D <rid> use row id <rid> when duplicating. (Default is 1 if inserting at end, <numrows> if inserting at beginning\n");
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ num_rows_set = true;
+ } else if (strcmp(argv[0], "-c")==0) {
+ CHECK_RESULTS = 1;
+ } else if (strcmp(argv[0], "-z")==0) {
+ COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-p")==0) {
+ DISALLOW_PUTS = LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-s")==0) {
+ db_env_set_loader_size_factor(1);
+ } else if (strcmp(argv[0], "-E")==0) {
+ dup_row_at_end = true;
+ } else if (strcmp(argv[0], "-D")==0) {
+ argc--; argv++;
+ dup_row_id = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ assert(0<=dup_row_id && dup_row_id<=NUM_ROWS);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-no-puts.cc b/storage/tokudb/PerconaFT/src/tests/loader-no-puts.cc
new file mode 100644
index 00000000000..2a29b150f2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-no-puts.cc
@@ -0,0 +1,245 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+
+static const char *envdir = TOKU_TEST_FILENAME;
+
+DB_ENV *env;
+int DISALLOW_PUTS=0;
+int COMPRESS=0;
+enum {MAX_NAME=128};
+enum {NUM_DBS=1};
+enum {NUM_KV_PAIRS=3};
+struct kv_pair {
+ int64_t key;
+ int64_t val;
+};
+struct kv_pair kv_pairs[NUM_KV_PAIRS] = {{1,4},
+ {2,5},
+ {3,6}};
+
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+
+ (void) src_db;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+ assert(which == 0);
+
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+static void test_loader(DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[NUM_DBS];
+ uint32_t dbt_flags[NUM_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = DISALLOW_PUTS | COMPRESS; // set with -p option
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ r = loader->set_error_callback(loader, NULL, NULL);
+ CKERR(r);
+ r = loader->set_poll_function(loader, NULL, NULL);
+ CKERR(r);
+
+/* // using loader->put, put values into DB
+ DBT key, val;
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ dbt_init(&key, &kv_pairs[i].key, sizeof(kv_pairs[i].key));
+ dbt_init(&val, &kv_pairs[i].val, sizeof(kv_pairs[i].val));
+ r = loader->put(loader, &key, &val);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, EINVAL);
+ } else {
+ CKERR(r);
+ }
+ }
+*/
+ // close the loader
+ r = loader->close(loader);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // verify the DBs
+/*
+ DBC *cursor;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ for(int j=0;j<NUM_DBS;j++) {
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, DB_NOTFOUND);
+ } else {
+ if (r!=0) { fprintf(stderr, "r==%d, failure\n", r); }
+ CKERR(r);
+ assert(*(int64_t*)key.data == kv_pairs[i].key);
+ assert(*(int64_t*)val.data == kv_pairs[i].val);
+ }
+ }
+ cursor->c_close(cursor);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+*/
+
+ printf("PASS\n");
+}
+
+static void run_test(void)
+{
+ int r;
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+// int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB *dbs[NUM_DBS];
+ int idx[NUM_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ // -------------------------- //
+ test_loader(dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ r = env->close(env, 0); CKERR(r);
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-z")==0) {
+ COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-p")==0) {
+ DISALLOW_PUTS = LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-e") == 0) {
+ argc--; argv++;
+ if (argc > 0)
+ envdir = argv[0];
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-reference-test.cc b/storage/tokudb/PerconaFT/src/tests/loader-reference-test.cc
new file mode 100644
index 00000000000..22be28ed716
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-reference-test.cc
@@ -0,0 +1,254 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+
+static const char *envdir = TOKU_TEST_FILENAME;
+
+DB_ENV *env;
+int DISALLOW_PUTS=0;
+int COMPRESS=0;
+enum {MAX_NAME=128};
+enum {NUM_DBS=1};
+enum {NUM_KV_PAIRS=3};
+struct kv_pair {
+ int64_t key;
+ int64_t val;
+};
+struct kv_pair kv_pairs[NUM_KV_PAIRS] = {{1,4},
+ {2,5},
+ {3,6}};
+static uint32_t block_size = 0;
+
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+
+ (void) src_db;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+ assert(which == 0);
+
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+static void test_loader(DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[NUM_DBS];
+ uint32_t dbt_flags[NUM_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = DISALLOW_PUTS | COMPRESS; // set with -p or -c option
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ r = loader->set_error_callback(loader, NULL, NULL);
+ CKERR(r);
+ r = loader->set_poll_function(loader, NULL, NULL);
+ CKERR(r);
+
+ uint64_t before_puts = toku_test_get_latest_lsn(env);
+ // using loader->put, put values into DB
+ DBT key, val;
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ dbt_init(&key, &kv_pairs[i].key, sizeof(kv_pairs[i].key));
+ dbt_init(&val, &kv_pairs[i].val, sizeof(kv_pairs[i].val));
+ r = loader->put(loader, &key, &val);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, EINVAL);
+ } else {
+ CKERR(r);
+ }
+ }
+ uint64_t after_puts = toku_test_get_latest_lsn(env);
+ assert(before_puts == after_puts);
+
+ // close the loader
+ r = loader->close(loader);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // verify the DBs
+ DBC *cursor;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ for(int j=0;j<NUM_DBS;j++) {
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+ for(int i=0;i<NUM_KV_PAIRS;i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r!=0) { fprintf(stderr, "r==%d, failure\n", r); }
+ if (DISALLOW_PUTS) {
+ CKERR2(r, DB_NOTFOUND);
+ } else {
+ CKERR(r);
+ assert(*(int64_t*)key.data == kv_pairs[i].key);
+ assert(*(int64_t*)val.data == kv_pairs[i].val);
+ }
+ }
+ cursor->c_close(cursor);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ if ( verbose ) printf("PASS\n");
+}
+
+static void run_test(void)
+{
+ int r;
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char logdir[8 + strlen(envdir)];
+ snprintf(logdir, sizeof logdir, "%s/log", envdir);
+ r = toku_os_mkdir(logdir, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_dir(env, "log");
+ CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+// int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB *dbs[NUM_DBS];
+ int idx[NUM_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ if (block_size != 0) {
+ r = dbs[i]->set_pagesize(dbs[i], block_size); CKERR(r);
+ }
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ // -------------------------- //
+ test_loader(dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ r = env->close(env, 0); CKERR(r);
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-p")==0) {
+ DISALLOW_PUTS = LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-z")==0) {
+ COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "--block_size") == 0) {
+ argc--; argv++;
+ block_size = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-e") == 0) {
+ argc--; argv++;
+ if (argc > 0)
+ envdir = argv[0];
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-stress-del.cc b/storage/tokudb/PerconaFT/src/tests/loader-stress-del.cc
new file mode 100644
index 00000000000..aaf75d130cb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-stress-del.cc
@@ -0,0 +1,733 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Need to use malloc for the malloc instrumentation tests
+#ifndef TOKU_ALLOW_DEPRECATED
+#define TOKU_ALLOW_DEPRECATED
+#endif
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+#include <memory.h>
+#include <dlfcn.h>
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+enum {MAX_DBS=1024};
+int NUM_DBS=1;
+int NUM_ROWS=1000000;
+int CHECK_RESULTS=1;
+int DISALLOW_PUTS=0;
+int COMPRESS=0;
+enum { old_default_cachesize=1024 }; // MB
+int CACHESIZE=old_default_cachesize;
+int ALLOW_DUPS=0;
+enum {MAGIC=311};
+char *datadir = NULL;
+bool check_est = true; // do check the estimates by default
+bool footprint_print = false; // print memory footprint info
+bool upgrade_test = false;
+
+// Code for showing memory footprint information.
+pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER;
+size_t hiwater;
+size_t water;
+size_t hiwater_start;
+static long long mcount = 0, fcount=0;
+
+
+static void my_free(void*p) {
+ if (p) {
+ water-=toku_malloc_usable_size(p);
+ }
+ free(p);
+}
+
+static void *my_malloc(size_t size) {
+ void *r = malloc(size);
+ if (r) {
+ water += toku_malloc_usable_size(r);
+ if (water>hiwater) hiwater=water;
+ }
+ return r;
+}
+
+static void *my_realloc(void *p, size_t size) {
+ size_t old_usable = p ? toku_malloc_usable_size(p) : 0;
+ void *r = realloc(p, size);
+ if (r) {
+ water -= old_usable;
+ water += toku_malloc_usable_size(r);
+ }
+ return r;
+}
+
+//
+// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
+//
+
+// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
+// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
+int a[MAX_DBS][32];
+int inv[MAX_DBS][32];
+
+
+static const char *loader_temp_prefix = "tokuld"; // #2536
+
+// return number of temp files
+static int
+count_temp(char * dirname) {
+ int n = 0;
+
+ DIR * dir = opendir(dirname);
+
+ struct dirent *ent;
+ while ((ent=readdir(dir))) {
+ if ((ent->d_type==DT_REG || ent->d_type==DT_UNKNOWN) && strncmp(ent->d_name, loader_temp_prefix, 6)==0) {
+ n++;
+ if (verbose) {
+ printf("Temp files (%d)\n", n);
+ printf(" %s/%s\n", dirname, ent->d_name);
+ }
+ }
+ }
+ closedir(dir);
+ return n;
+}
+
+// rotate right and left functions
+static inline unsigned int rotr32(const unsigned int x, const unsigned int num) {
+ if (num == 0) {
+ return x;
+ } else {
+ const unsigned int n = num % 32;
+ return (x >> n) | ( x << (32 - n));
+ }
+}
+static inline unsigned int rotl32(const unsigned int x, const unsigned int num) {
+ if (num == 0) {
+ return x;
+ } else {
+ const unsigned int n = num % 32;
+ return (x << n) | ( x >> (32 - n));
+ }
+}
+
+static void generate_permute_tables(void) {
+ int i, j, tmp;
+ for(int db=0;db<MAX_DBS;db++) {
+ for(i=0;i<32;i++) {
+ a[db][i] = i;
+ }
+ for(i=0;i<32;i++) {
+ j = random() % (i + 1);
+ tmp = a[db][j];
+ a[db][j] = a[db][i];
+ a[db][i] = tmp;
+ }
+// if(db < NUM_DBS){ printf("a[%d] = ", db); for(i=0;i<32;i++) { printf("%2d ", a[db][i]); } printf("\n");}
+ for(i=0;i<32;i++) {
+ inv[db][a[db][i]] = i;
+ }
+ }
+}
+
+// permute bits of x based on permute table bitmap
+static unsigned int twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << a[db][i];
+ }
+ return b;
+}
+
+// permute bits of x based on inverse permute table bitmap
+static unsigned int inv_twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << inv[db][i];
+ }
+ return b;
+}
+
+// generate val from key, index
+static unsigned int generate_val(int key, int i) {
+ return rotl32((key + MAGIC), i);
+}
+static unsigned int pkey_for_val(int key, int i) {
+ return rotr32(key, i) - MAGIC;
+}
+
+// There is no handlerton in this test, so this function is a local replacement
+// for the handlerton's generate_row_for_put().
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ (void) src_db;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ if ( which == 0 ) {
+ if (dest_key->flags==DB_DBT_REALLOC) {
+ if (dest_key->data) toku_free(dest_key->data);
+ dest_key->flags = 0;
+ dest_key->ulen = 0;
+ }
+ if (dest_val->flags==DB_DBT_REALLOC) {
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_val->flags = 0;
+ dest_val->ulen = 0;
+ }
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+ }
+ else {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(unsigned int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(unsigned int));
+ dest_key->ulen = sizeof(unsigned int);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < sizeof(unsigned int)) {
+ dest_val->data = toku_xrealloc(dest_val->data, sizeof(unsigned int));
+ dest_val->ulen = sizeof(unsigned int);
+ }
+ unsigned int *new_key = (unsigned int *)dest_key->data;
+ unsigned int *new_val = (unsigned int *)dest_val->data;
+
+ *new_key = twiddle32(*(unsigned int*)src_key->data, which);
+ *new_val = generate_val(*(unsigned int*)src_key->data, which);
+
+ dest_key->size = sizeof(unsigned int);
+ dest_val->size = sizeof(unsigned int);
+ //data is already set above
+ }
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+
+static int uint_cmp(const void *ap, const void *bp) {
+ unsigned int an = *(unsigned int *)ap;
+ unsigned int bn = *(unsigned int *)bp;
+ if (an < bn)
+ return -1;
+ if (an > bn)
+ return +1;
+ return 0;
+}
+
+static void check_results(DB **dbs) {
+ for(int j=0;j<NUM_DBS;j++) {
+ unsigned int prev_k = 0;
+
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+
+ int r;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+
+ // generate the expected keys
+ unsigned int *expected_key = (unsigned int *) toku_malloc(NUM_ROWS * sizeof (unsigned int));
+ for (int i = 0; i < NUM_ROWS; i++) {
+ expected_key[i] = j == 0 ? (unsigned int)(i+1) : twiddle32(i+1, j);
+ }
+ // sort the keys
+ qsort(expected_key, NUM_ROWS, sizeof (unsigned int), uint_cmp);
+
+ for (int i = 0; i < NUM_ROWS+1; i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, DB_NOTFOUND);
+ break;
+ }
+ if (r == DB_NOTFOUND) {
+ assert(i == NUM_ROWS); // check that there are exactly NUM_ROWS in the dictionary
+ break;
+ }
+ CKERR(r);
+
+ k = *(unsigned int*)key.data;
+
+ unsigned int pkey_for_db_key = (j == 0) ? k : inv_twiddle32(k, j);
+ v = *(unsigned int*)val.data;
+ // test that we have the expected keys and values
+ assert((unsigned int)pkey_for_db_key == (unsigned int)pkey_for_val(v, j));
+// printf(" DB[%d] key = %10u, val = %10u, pkey_for_db_key = %10u, pkey_for_val=%10d\n", j, v, k, pkey_for_db_key, pkey_for_val(v, j));
+
+ // check the expected keys
+ assert(k == expected_key[i]);
+
+ // check prev_key < key
+ if (i > 0)
+ assert(prev_k < k);
+
+ // update prev = current
+ prev_k = k;
+ }
+
+ toku_free(expected_key);
+
+ if ( verbose ) {printf("."); fflush(stdout);}
+ r = cursor->c_close(cursor);
+ CKERR(r);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ if ( verbose ) printf("\nCheck OK\n");
+}
+
+static void delete_all(DB **dbs) {
+ for(int j=0;j<NUM_DBS;j++) {
+
+ int r;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ // generate the expected keys
+ unsigned int *expected_key = (unsigned int *) toku_malloc(NUM_ROWS * sizeof (unsigned int));
+ for (int i = 0; i < NUM_ROWS; i++)
+ expected_key[i] = j == 0 ? (unsigned int)(i+1) : twiddle32(i+1, j);
+ // sort the keys
+ qsort(expected_key, NUM_ROWS, sizeof (unsigned int), uint_cmp);
+
+ // delete all of the keys
+ for (int i = 0; i < NUM_ROWS; i++) {
+ DBT key;
+ dbt_init(&key, &expected_key[i], sizeof expected_key[i]);
+ r = dbs[j]->del(dbs[j], txn, &key, DB_DELETE_ANY);
+ assert(r == 0);
+ }
+
+ // verify empty
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ assert(r == DB_NOTFOUND);
+
+ toku_free(expected_key);
+
+ if ( verbose ) {printf("."); fflush(stdout);}
+ r = cursor->c_close(cursor);
+ CKERR(r);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ if ( verbose ) printf("\nCheck OK\n");
+}
+
+static void *expect_poll_void = &expect_poll_void;
+static uint64_t poll_count=0;
+static uint64_t bomb_after_poll_count=UINT64_MAX;
+
+static struct progress_info {
+ double time;
+ double progress;
+} *progress_infos=NULL;
+static int progress_infos_count=0;
+static int progress_infos_limit=0;
+
+// timing
+static bool did_start=false;
+static struct timeval start;
+
+static int poll_function (void *extra, float progress) {
+ if (verbose>=2) {
+ assert(did_start);
+ struct timeval now;
+ gettimeofday(&now, 0);
+ double elapsed = now.tv_sec - start.tv_sec + 1e-6*(now.tv_usec - start.tv_usec);
+ printf("Progress: %6.6fs %5.1f%%\n", elapsed, progress*100);
+ if (progress_infos_count>=progress_infos_limit) {
+ progress_infos_limit = 2*progress_infos_limit + 1;
+ XREALLOC_N(progress_infos_limit, progress_infos);
+ }
+ progress_infos[progress_infos_count++] = (struct progress_info){elapsed, progress};
+ }
+ assert(extra==expect_poll_void);
+ assert(0.0<=progress && progress<=1.0);
+ poll_count++; // Calls to poll_function() are protected by a lock, so we don't have to do this atomically.
+ if (poll_count>bomb_after_poll_count)
+ return TOKUDB_CANCELED;
+ else
+ return 0;
+}
+
+static struct timeval starttime;
+static double elapsed_time (void) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return now.tv_sec - starttime.tv_sec + 1e-6*(now.tv_usec - starttime.tv_usec);
+}
+
+static void test_loader(DB **dbs)
+{
+ gettimeofday(&starttime, NULL);
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[MAX_DBS];
+ uint32_t dbt_flags[MAX_DBS];
+ uint32_t flags = DB_NOOVERWRITE;
+ if ( (DISALLOW_PUTS != 0) && (ALLOW_DUPS == 1) ) flags = 0;
+ for(int i=0;i<MAX_DBS;i++) {
+ db_flags[i] = flags;
+ dbt_flags[i] = 0;
+ }
+
+ uint32_t loader_flags = DISALLOW_PUTS | COMPRESS; // set with -p option
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ hiwater_start = hiwater;
+ if (footprint_print) printf("%s:%d Hiwater=%ld water=%ld\n", __FILE__, __LINE__, hiwater, water);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ if (footprint_print) printf("%s:%d Hiwater=%ld water=%ld\n", __FILE__, __LINE__, hiwater, water);
+ r = loader->set_error_callback(loader, NULL, NULL);
+ CKERR(r);
+ r = loader->set_poll_function(loader, poll_function, expect_poll_void);
+ CKERR(r);
+
+ // using loader->put, put values into DB
+ DBT key, val;
+ unsigned int k, v;
+ for(int i=1;i<=NUM_ROWS;i++) {
+ k = i;
+ v = generate_val(i, 0);
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, EINVAL);
+ } else {
+ CKERR(r);
+ }
+ if ( verbose) { if((i%10000) == 0){printf("."); fflush(stdout);} }
+ }
+ if ( verbose ) {printf("\n"); fflush(stdout);}
+
+ poll_count=0;
+
+ int n = count_temp(env->i->real_data_dir);
+ if (verbose) printf("Num temp files = %d\n", n);
+
+ did_start = true;
+ gettimeofday(&start, 0);
+
+ // close the loader
+ if ( verbose ) printf("%9.6fs closing\n", elapsed_time());
+ if (footprint_print) printf("%s:%d Hiwater=%ld water=%ld\n", __FILE__, __LINE__, hiwater, water);
+ r = loader->close(loader);
+ if (footprint_print) printf("%s:%d Hiwater=%ld water=%ld (extra hiwater=%ldM)\n", __FILE__, __LINE__, hiwater, water, (hiwater-hiwater_start)/(1024*1024));
+ if ( verbose ) printf("%9.6fs done\n", elapsed_time());
+ CKERR2s(r,0,TOKUDB_CANCELED);
+
+ if (r==0) {
+ if ( DISALLOW_PUTS == 0 ) {
+ if (poll_count == 0) printf("%s:%d\n", __FILE__, __LINE__);
+ assert(poll_count>0);
+ }
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // verify the DBs
+ if ( CHECK_RESULTS ) {
+ check_results(dbs);
+ delete_all(dbs);
+ }
+
+ } else {
+ r = txn->abort(txn);
+ CKERR(r);
+ }
+}
+
+static const char *envdir = TOKU_TEST_FILENAME;
+const char *tmp_subdir = "tmp.subdir";
+
+#define OLDDATADIR "../../../../tokudb.data/"
+const char *db_v4_dir = OLDDATADIR "env_preload.4.1.1.emptydictionaries.cleanshutdown";
+
+static void setup(void) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+ const char * src_db_dir;
+
+ src_db_dir = db_v4_dir;
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, envdir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+}
+
+static void run_test(void)
+{
+ int r;
+
+ int cmdlen = strlen(envdir) + strlen(tmp_subdir) + 10;
+ char tmpdir[cmdlen];
+ r = snprintf(tmpdir, cmdlen, "%s/%s", envdir, tmp_subdir);
+ assert(r<cmdlen);
+
+ // first delete anything left from previous run of this test
+ {
+ int len = strlen(envdir) + 20;
+ char syscmd[len];
+ r = snprintf(syscmd, len, "rm -rf %s", envdir);
+ assert(r<len);
+ r = system(syscmd); CKERR(r);
+ }
+ if (upgrade_test) {
+ setup();
+ }
+ else {
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = toku_os_mkdir(tmpdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ }
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_tmp_dir(env, tmp_subdir); CKERR(r);
+
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ if ( verbose ) printf("CACHESIZE = %d MB\n", CACHESIZE);
+ r = env->set_cachesize(env, CACHESIZE / 1024, (CACHESIZE % 1024)*1024*1024, 1); CKERR(r);
+ if (datadir) {
+ r = env->set_data_dir(env, datadir); CKERR(r);
+ }
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 60); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ generate_permute_tables();
+
+ // -------------------------- //
+ test_loader(dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+
+ run_test();
+
+ if (progress_infos) {
+ if (verbose>=2) {
+ double ratio=progress_infos[progress_infos_count-1].time/progress_infos[progress_infos_count-1].progress;
+ printf("Progress ratios:\n");
+ for (int i=0; i<progress_infos_count; i++) {
+ printf(" %5.3f\n", (progress_infos[i].time/progress_infos[i].progress)/ratio);
+ }
+ }
+ toku_free(progress_infos);
+ }
+ if (footprint_print) {
+ printf("%s:%d Hiwater=%ld water=%ld (extra hiwater=%ldM) mcount=%lld fcount=%lld\n", __FILE__, __LINE__, hiwater, water, (hiwater-hiwater_start)/(1024*1024), mcount, fcount);
+ typedef void (*malloc_stats_fun_t)(void);
+ malloc_stats_fun_t malloc_stats_f = (malloc_stats_fun_t) dlsym(RTLD_DEFAULT, "malloc_stats");
+ if (malloc_stats_f) {
+ malloc_stats_f();
+ }
+ }
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+
+ // Must look for "-f" right away before we malloc anything.
+ for (int i=1; i<argc; i++) {
+
+ if (strcmp(argv[i], "-f")) {
+ db_env_set_func_malloc(my_malloc);
+ db_env_set_func_realloc(my_realloc);
+ db_env_set_func_free(my_free);
+ }
+ }
+
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ CACHESIZE = (toku_os_get_phys_memory_size() / (1024*1024))/2; //MB
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -c -d <num_dbs> -r <num_rows> [ -b <num_calls> ] [-m <megabytes>] [-M]\n%s\n", cmd);
+ fprintf(stderr, " where -d <num_dbs> is the number of dictionaries to build (primary & secondary). (Default=%d)\n", NUM_DBS);
+ fprintf(stderr, " -b <num_calls> causes the poll function to return nonzero after <num_calls>\n");
+ fprintf(stderr, " -e <env> uses <env> to construct the directory (so that different tests can run concurrently)\n");
+ fprintf(stderr, " -m <m> use m MB of memory for the cachetable (default is %d MB)\n", CACHESIZE);
+ fprintf(stderr, " -M use %d MB of memory for the cachetable\n", old_default_cachesize);
+ fprintf(stderr, " -s use size factor of 1 and count temporary files\n");
+ fprintf(stderr, " -f print memory footprint information at various points in the load\n");
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-e")==0) {
+ argc--; argv++;
+ envdir = argv[0];
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-f")==0) {
+ footprint_print = true;
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-c")==0) {
+ CHECK_RESULTS = 1;
+ } else if (strcmp(argv[0], "-p")==0) {
+ DISALLOW_PUTS = LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-z")==0) {
+ COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-m")==0) {
+ argc--; argv++;
+ CACHESIZE = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-M")==0) {
+ CACHESIZE = old_default_cachesize;
+ } else if (strcmp(argv[0], "-y")==0) {
+ ALLOW_DUPS = 1;
+ } else if (strcmp(argv[0], "-s")==0) {
+ //printf("\nTesting loader with size_factor=1\n");
+ db_env_set_loader_size_factor(1);
+ } else if (strcmp(argv[0], "-b")==0) {
+ argc--; argv++;
+ char *end;
+ errno=0;
+ bomb_after_poll_count = strtoll(argv[0], &end, 10);
+ assert(errno==0);
+ assert(*end==0); // make sure we consumed the whole integer.
+ } else if (strcmp(argv[0], "--datadir") == 0 && argc > 1) {
+ argc--; argv++;
+ datadir = argv[0];
+ } else if (strcmp(argv[0], "--dont_check_est") == 0) {
+ check_est = false;
+ } else if (strcmp(argv[0], "-u")==0) {
+ upgrade_test = true;
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-stress-test.cc b/storage/tokudb/PerconaFT/src/tests/loader-stress-test.cc
new file mode 100644
index 00000000000..dfd7053d196
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-stress-test.cc
@@ -0,0 +1,697 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/**************
+ *
+ * NOTE: This test is used for upgrade testing as well as for exercising the loader.
+ * Changes should not be made gratuitously.
+ * The 4.2.0 version of this test was used to create many of the preloaded
+ * environments in the <svn-top>/tokudb/tokudb.data directory.
+ */
+
+
+// Need to use malloc for the malloc instrumentation tests
+#ifndef TOKU_ALLOW_DEPRECATED
+#define TOKU_ALLOW_DEPRECATED
+#endif
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+#include <memory.h>
+#include <dlfcn.h>
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+enum {MAX_DBS=1024};
+int NUM_DBS=5;
+int NUM_ROWS=100000;
+int VALSIZE=sizeof(unsigned int);
+int CHECK_RESULTS=0;
+int DISALLOW_PUTS=0;
+int COMPRESS=0;
+enum { old_default_cachesize=1024 }; // MB
+int CACHESIZE=old_default_cachesize;
+int ALLOW_DUPS=0;
+enum {MAGIC=311};
+char *datadir = NULL;
+bool check_est = true; // do check the estimates by default
+bool footprint_print = false; // print memory footprint info
+bool upgrade_test = false;
+
+// Code for showing memory footprint information.
+pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER;
+size_t hiwater;
+size_t water;
+size_t hiwater_start;
+static long long mcount = 0, fcount=0;
+
+static void my_free(void*p) {
+ if (p) {
+ water-=toku_malloc_usable_size(p);
+ }
+ free(p);
+}
+
+static void *my_malloc(size_t size) {
+ void *r = malloc(size);
+ if (r) {
+ water += toku_malloc_usable_size(r);
+ if (water>hiwater) hiwater=water;
+ }
+ return r;
+}
+
+static void *my_realloc(void *p, size_t size) {
+ size_t old_usable = p ? toku_malloc_usable_size(p) : 0;
+ void *r = realloc(p, size);
+ if (r) {
+ water -= old_usable;
+ water += toku_malloc_usable_size(r);
+ }
+ return r;
+}
+
+//
+// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
+//
+
+// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
+// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
+int a[MAX_DBS][32];
+int inv[MAX_DBS][32];
+
+static const char *loader_temp_prefix = "tokuld"; // #2536
+
+// return number of temp files
+static int
+count_temp(char * dirname) {
+ int n = 0;
+
+ DIR * dir = opendir(dirname);
+
+ struct dirent *ent;
+ while ((ent=readdir(dir))) {
+ if ((ent->d_type==DT_REG || ent->d_type==DT_UNKNOWN) && strncmp(ent->d_name, loader_temp_prefix, 6)==0) {
+ n++;
+ if (verbose) {
+ printf("Temp files (%d)\n", n);
+ printf(" %s/%s\n", dirname, ent->d_name);
+ }
+ }
+ }
+ closedir(dir);
+ return n;
+}
+
+// rotate right and left functions
+static inline unsigned int rotr32(const unsigned int x, const unsigned int num) {
+ if (num == 0) {
+ return x;
+ } else {
+ const unsigned int n = num % 32;
+ return (x >> n) | ( x << (32 - n));
+ }
+}
+static inline unsigned int rotl32(const unsigned int x, const unsigned int num) {
+ if (num == 0) {
+ return x;
+ } else {
+ const unsigned int n = num % 32;
+ return (x << n) | ( x >> (32 - n));
+ }
+}
+
+static void generate_permute_tables(void) {
+ int i, j, tmp;
+ for(int db=0;db<MAX_DBS;db++) {
+ for(i=0;i<32;i++) {
+ a[db][i] = i;
+ }
+ for(i=0;i<32;i++) {
+ j = random() % (i + 1);
+ tmp = a[db][j];
+ a[db][j] = a[db][i];
+ a[db][i] = tmp;
+ }
+// if(db < NUM_DBS){ printf("a[%d] = ", db); for(i=0;i<32;i++) { printf("%2d ", a[db][i]); } printf("\n");}
+ for(i=0;i<32;i++) {
+ inv[db][a[db][i]] = i;
+ }
+ }
+}
+
+// permute bits of x based on permute table bitmap
+static unsigned int twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << a[db][i];
+ }
+ return b;
+}
+
+// permute bits of x based on inverse permute table bitmap
+static unsigned int inv_twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << inv[db][i];
+ }
+ return b;
+}
+
+// generate val from key, index
+static void generate_val(int key, int i, unsigned int*v) {
+ v[0] = rotl32((key + MAGIC), i);
+ for (unsigned w = 1; w < VALSIZE/sizeof(unsigned int); w++) {
+ v[w] = rotr32(v[w-1], 1);
+ }
+}
+
+static unsigned int pkey_for_val(int key, int i) {
+ return rotr32(key, i) - MAGIC;
+}
+
+// There is no handlerton in this test, so this function is a local replacement
+// for the handlerton's generate_row_for_put().
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *UU(src_val)) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ assert(src_db);
+ assert(dest_db != src_db);
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+ assert(which != 0);
+
+ {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(unsigned int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(unsigned int));
+ dest_key->ulen = sizeof(unsigned int);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < (unsigned)VALSIZE) {
+ dest_val->data = toku_xrealloc(dest_val->data, VALSIZE);
+ dest_val->ulen = VALSIZE;
+ }
+ unsigned int *new_key = (unsigned int *)dest_key->data;
+
+ *new_key = twiddle32(*(unsigned int*)src_key->data, which);
+ generate_val(*(unsigned int*)src_key->data, which, (unsigned int*)dest_val->data);
+
+ dest_key->size = sizeof(unsigned int);
+ dest_val->size = VALSIZE;
+ //data is already set above
+ }
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+static int uint_cmp(const void *ap, const void *bp) {
+ unsigned int an = *(unsigned int *)ap;
+ unsigned int bn = *(unsigned int *)bp;
+ if (an < bn)
+ return -1;
+ if (an > bn)
+ return +1;
+ return 0;
+}
+
+static void check_results(DB **dbs) {
+ // verify trees
+ for (int j = 0;j < NUM_DBS; j++) {
+ int r = dbs[j]->verify_with_progress(dbs[j], NULL, NULL, 0, 0);
+ assert(r == 0);
+ }
+
+ // verify rows
+ for (int j = 0;j < NUM_DBS; j++) {
+ unsigned int prev_k = 0;
+
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+
+ int r;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+
+ // generate the expected keys
+ unsigned int *expected_key = (unsigned int *) toku_malloc(NUM_ROWS * sizeof (unsigned int));
+ for (int i = 0; i < NUM_ROWS; i++)
+ expected_key[i] = j == 0 ? (unsigned int)(i+1) : twiddle32(i+1, j);
+ // sort the keys
+ qsort(expected_key, NUM_ROWS, sizeof (unsigned int), uint_cmp);
+
+ unsigned int valcheck[VALSIZE/sizeof(unsigned int)];
+ for (int i = 0; i < NUM_ROWS+1; i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, DB_NOTFOUND);
+ break;
+ }
+ if (r == DB_NOTFOUND) {
+ assert(i == NUM_ROWS); // check that there are exactly NUM_ROWS in the dictionary
+ break;
+ }
+ CKERR(r);
+
+ k = *(unsigned int*)key.data;
+
+ unsigned int pkey_for_db_key = (j == 0) ? k : inv_twiddle32(k, j);
+ v = *(unsigned int*)val.data;
+ // test that we have the expected keys and values
+ assert((unsigned int)pkey_for_db_key == (unsigned int)pkey_for_val(v, j));
+
+
+// printf(" DB[%d] key = %10u, val = %10u, pkey_for_db_key = %10u, pkey_for_val=%10d\n", j, v, k, pkey_for_db_key, pkey_for_val(v, j));
+
+ // check the expected keys
+ assert(k == expected_key[i]);
+ generate_val(pkey_for_db_key, j, &valcheck[0]);
+ assert(val.size == (unsigned)VALSIZE);
+ assert(memcmp(val.data, &valcheck[0], VALSIZE)==0);
+
+ // check prev_key < key
+ if (i > 0)
+ assert(prev_k < k);
+
+ // update prev = current
+ prev_k = k;
+ }
+
+ toku_free(expected_key);
+
+ if ( verbose ) {printf("."); fflush(stdout);}
+ r = cursor->c_close(cursor);
+ CKERR(r);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ if ( verbose ) printf("\nCheck OK\n");
+}
+
+static void *expect_poll_void = &expect_poll_void;
+static uint64_t poll_count=0;
+static uint64_t bomb_after_poll_count=UINT64_MAX;
+
+static struct progress_info {
+ double time;
+ double progress;
+} *progress_infos=NULL;
+static int progress_infos_count=0;
+static int progress_infos_limit=0;
+
+// timing
+static bool did_start=false;
+static struct timeval start;
+
+static int poll_function (void *extra, float progress) {
+ if (verbose>=2) {
+ assert(did_start);
+ struct timeval now;
+ gettimeofday(&now, 0);
+ double elapsed = now.tv_sec - start.tv_sec + 1e-6*(now.tv_usec - start.tv_usec);
+ printf("Progress: %6.6fs %5.1f%%\n", elapsed, progress*100);
+ if (progress_infos_count>=progress_infos_limit) {
+ progress_infos_limit = 2*progress_infos_limit + 1;
+ XREALLOC_N(progress_infos_limit, progress_infos);
+ }
+ progress_infos[progress_infos_count++] = (struct progress_info){elapsed, progress};
+ }
+ assert(extra==expect_poll_void);
+ assert(0.0<=progress && progress<=1.0);
+ poll_count++; // Calls to poll_function() are protected by a lock, so we don't have to do this atomically.
+ if (poll_count>bomb_after_poll_count)
+ return TOKUDB_CANCELED;
+ else
+ return 0;
+}
+
+static struct timeval starttime;
+static double elapsed_time (void) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return now.tv_sec - starttime.tv_sec + 1e-6*(now.tv_usec - starttime.tv_usec);
+}
+
+static void test_loader(DB **dbs)
+{
+ gettimeofday(&starttime, NULL);
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[MAX_DBS];
+ uint32_t dbt_flags[MAX_DBS];
+ uint32_t flags = DB_NOOVERWRITE;
+ if ( (DISALLOW_PUTS) && (ALLOW_DUPS == 1) ) flags = 0;
+ for(int i=0;i<MAX_DBS;i++) {
+ db_flags[i] = flags;
+ dbt_flags[i] = 0;
+ }
+
+ uint32_t loader_flags = DISALLOW_PUTS | COMPRESS; // set with -p option
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ hiwater_start = hiwater;
+ if (footprint_print) printf("%s:%d Hiwater=%ld water=%ld\n", __FILE__, __LINE__, hiwater, water);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ if (footprint_print) printf("%s:%d Hiwater=%ld water=%ld\n", __FILE__, __LINE__, hiwater, water);
+ r = loader->set_error_callback(loader, NULL, NULL);
+ CKERR(r);
+ r = loader->set_poll_function(loader, poll_function, expect_poll_void);
+ CKERR(r);
+
+ // using loader->put, put values into DB
+ DBT key, val;
+ unsigned int k;
+ unsigned int v[VALSIZE/sizeof(unsigned int)];
+ for(int i=1;i<=NUM_ROWS;i++) {
+ k = i;
+ generate_val(i, 0, &v[0]);
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v[0], VALSIZE);
+ r = loader->put(loader, &key, &val);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, EINVAL);
+ } else {
+ CKERR(r);
+ }
+ if ( verbose) { if((i%10000) == 0){printf("."); fflush(stdout);} }
+ }
+ if ( verbose ) {printf("\n"); fflush(stdout);}
+
+ poll_count=0;
+
+ int n = count_temp(env->i->real_data_dir);
+ if (verbose) printf("Num temp files = %d\n", n);
+
+ did_start = true;
+ gettimeofday(&start, 0);
+
+ // close the loader
+ if ( verbose ) printf("%9.6fs closing\n", elapsed_time());
+ if (footprint_print) printf("%s:%d Hiwater=%ld water=%ld\n", __FILE__, __LINE__, hiwater, water);
+ r = loader->close(loader);
+ if (footprint_print) printf("%s:%d Hiwater=%ld water=%ld (extra hiwater=%ldM)\n", __FILE__, __LINE__, hiwater, water, (hiwater-hiwater_start)/(1024*1024));
+ if ( verbose ) printf("%9.6fs done\n", elapsed_time());
+ CKERR2s(r,0,TOKUDB_CANCELED);
+
+ if (r==0) {
+ if (!DISALLOW_PUTS) {
+ if (poll_count == 0) printf("%s:%d\n", __FILE__, __LINE__);
+ assert(poll_count>0);
+ }
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // verify the DBs
+ if ( CHECK_RESULTS ) {
+ check_results(dbs);
+ }
+
+ if ( check_est ) {
+ for (int i=0; i<NUM_DBS; i++) {
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ DB_BTREE_STAT64 stats;
+ r = dbs[i]->stat64(dbs[i], txn, &stats);
+ CKERR(r);
+ if (verbose)
+ printf("NUM_ROWS=%d n_keys=%" PRIu64 " n_data=%" PRIu64 " dsize=%" PRIu64 " fsize=%" PRIu64 "\n",
+ NUM_ROWS, stats.bt_nkeys, stats.bt_ndata, stats.bt_dsize, stats.bt_fsize);
+ if (DISALLOW_PUTS) {
+ assert(stats.bt_nkeys == 0); // Fix as part of #4129. Was ==
+ assert(stats.bt_ndata == 0);
+ assert(stats.bt_dsize == 0);
+ } else {
+ assert(stats.bt_nkeys <= (uint64_t)NUM_ROWS); // Fix as part of #4129. Was ==
+ assert(stats.bt_ndata <= (uint64_t)NUM_ROWS);
+ assert(stats.bt_dsize == ((uint64_t)NUM_ROWS) * (sizeof(unsigned int) + VALSIZE));
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ }
+ } else {
+ r = txn->abort(txn);
+ CKERR(r);
+ }
+}
+
+static const char *envdir = TOKU_TEST_FILENAME;
+const char *tmp_subdir = "tmp.subdir";
+
+static void run_test(void)
+{
+ int r;
+
+ if (upgrade_test) {
+ // cmake set up the environment
+ }
+ else {
+ toku_os_recursive_delete(envdir);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char tmpdir[TOKU_PATH_MAX+1];
+ toku_path_join(tmpdir, 2, envdir, tmp_subdir);
+ r = toku_os_mkdir(tmpdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ }
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_tmp_dir(env, tmp_subdir); CKERR(r);
+
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ if ( verbose ) printf("CACHESIZE = %d MB\n", CACHESIZE);
+ r = env->set_cachesize(env, CACHESIZE / 1024, (CACHESIZE % 1024)*1024*1024, 1); CKERR(r);
+ if (datadir) {
+ r = env->set_data_dir(env, datadir); CKERR(r);
+ }
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 60); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ generate_permute_tables();
+
+ // -------------------------- //
+ test_loader(dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+
+ run_test();
+
+ if (progress_infos) {
+ if (verbose>=2) {
+ double ratio=progress_infos[progress_infos_count-1].time/progress_infos[progress_infos_count-1].progress;
+ printf("Progress ratios:\n");
+ for (int i=0; i<progress_infos_count; i++) {
+ printf(" %5.3f\n", (progress_infos[i].time/progress_infos[i].progress)/ratio);
+ }
+ }
+ toku_free(progress_infos);
+ }
+ if (footprint_print) {
+ printf("%s:%d Hiwater=%ld water=%ld (extra hiwater=%ldM) mcount=%lld fcount=%lld\n", __FILE__, __LINE__, hiwater, water, (hiwater-hiwater_start)/(1024*1024), mcount, fcount);
+ typedef void (*malloc_stats_fun_t)(void);
+ malloc_stats_fun_t malloc_stats_f = (malloc_stats_fun_t) dlsym(RTLD_DEFAULT, "malloc_stats");
+ if (malloc_stats_f) {
+ malloc_stats_f();
+ }
+ }
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+
+ // Must look for "-f" right away before we malloc anything.
+ for (int i=1; i<argc; i++) {
+
+ if (strcmp(argv[i], "-f")) {
+ db_env_set_func_malloc(my_malloc);
+ db_env_set_func_realloc(my_realloc);
+ db_env_set_func_free(my_free);
+ }
+ }
+
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ CACHESIZE = (toku_os_get_phys_memory_size() / (1024*1024))/2; //MB
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -c -d <num_dbs> -r <num_rows> [ -b <num_calls> ] [-m <megabytes>] [-M]\n%s\n", cmd);
+ fprintf(stderr, " where -d <num_dbs> is the number of dictionaries to build (primary & secondary). (Default=%d)\n", NUM_DBS);
+ fprintf(stderr, " -b <num_calls> causes the poll function to return nonzero after <num_calls>\n");
+ fprintf(stderr, " -m <m> use m MB of memory for the cachetable (default is %d MB)\n", CACHESIZE);
+ fprintf(stderr, " -M use %d MB of memory for the cachetable\n", old_default_cachesize);
+ fprintf(stderr, " -s use size factor of 1 and count temporary files\n");
+ fprintf(stderr, " -f print memory footprint information at various points in the load\n");
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-f")==0) {
+ footprint_print = true;
+ } else if (strcmp(argv[0], "--valsize")==0) {
+ argc--; argv++;
+ VALSIZE=atoi(argv[0]);
+ VALSIZE -= VALSIZE % sizeof(unsigned int);
+ if ( VALSIZE < (int)sizeof(unsigned int) ) {
+ fprintf(stderr, "--valsize must be multiple of %d\n", (int)sizeof(unsigned int));
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-c")==0) {
+ CHECK_RESULTS = 1;
+ } else if (strcmp(argv[0], "-p")==0) {
+ DISALLOW_PUTS = LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-z")==0) {
+ COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-m")==0) {
+ argc--; argv++;
+ CACHESIZE = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-M")==0) {
+ CACHESIZE = old_default_cachesize;
+ } else if (strcmp(argv[0], "-y")==0) {
+ ALLOW_DUPS = 1;
+ } else if (strcmp(argv[0], "-s")==0) {
+ //printf("\nTesting loader with size_factor=1\n");
+ db_env_set_loader_size_factor(1);
+ } else if (strcmp(argv[0], "-b")==0) {
+ argc--; argv++;
+ char *end;
+ errno=0;
+ bomb_after_poll_count = strtoll(argv[0], &end, 10);
+ assert(errno==0);
+ assert(*end==0); // make sure we consumed the whole integer.
+ } else if (strcmp(argv[0], "--datadir") == 0 && argc > 1) {
+ argc--; argv++;
+ datadir = argv[0];
+ } else if (strcmp(argv[0], "--dont_check_est") == 0) {
+ check_est = false;
+ } else if (strcmp(argv[0], "-u")==0) {
+ upgrade_test = true;
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/loader-tpch-load.cc b/storage/tokudb/PerconaFT/src/tests/loader-tpch-load.cc
new file mode 100644
index 00000000000..b2ecb25350e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/loader-tpch-load.cc
@@ -0,0 +1,508 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+enum {MAX_DBS=16};
+enum {MAX_ROW_LEN=1024};
+static int NUM_DBS=10;
+static int DISALLOW_PUTS=0;
+static int COMPRESS=0;
+static int USE_REGION=0;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static int generate_rows_for_region(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) __attribute__((unused));
+static int generate_rows_for_lineitem(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) __attribute__((unused));
+
+// linenumber,orderkey form a unique, primary key
+// key is a potentially duplicate secondary key
+struct tpch_key {
+ uint32_t linenumber;
+ uint32_t orderkey;
+ uint32_t key;
+};
+
+static __attribute__((__unused__)) int
+tpch_dbt_cmp (DB *db, const DBT *a, const DBT *b) {
+ assert(db && a && b);
+ assert(a->size == sizeof(struct tpch_key));
+ assert(b->size == sizeof(struct tpch_key));
+
+ unsigned int xl = (*((struct tpch_key *) a->data)).linenumber;
+ unsigned int xo = (*((struct tpch_key *) a->data)).orderkey;
+ unsigned int xk = (*((struct tpch_key *) a->data)).key;
+
+ unsigned int yl = (*((struct tpch_key *) b->data)).linenumber;
+ unsigned int yo = (*((struct tpch_key *) b->data)).orderkey;
+ unsigned int yk = (*((struct tpch_key *) b->data)).key;
+
+// printf("tpch_dbt_cmp xl:%d, yl:%d, xo:%d, yo:%d, xk:%d, yk:%d\n", xl, yl, xo, yo, xk, yk);
+
+ if (xk<yk) return -1;
+ if (xk>yk) return 1;
+
+ if (xl<yl) return -1;
+ if (xl>yl) return 1;
+
+ if (xo>yo) return -1;
+ if (xo<yo) return 1;
+ return 0;
+}
+
+
+static int lineno = 0;
+static char *tpch_read_row(FILE *fp, int *key, char *val)
+{
+ *key = lineno++;
+ return fgets(val, MAX_ROW_LEN , fp);
+}
+
+
+/*
+ * split '|' separated fields into fields array
+ */
+static void tpch_parse_row(char *row, char *fields[], int fields_N)
+{
+ int field = 0;
+ int i = 0;
+ int p = 0;
+ char c = row[p];
+
+ while(c != '\0')
+ {
+ if ( c == '|') {
+ fields[field][i] = '\0';
+ //printf("field : <%s>\n", fields[field]);
+ field++;
+ i = 0;
+ }
+ else
+ fields[field][i++] = c;
+ c = row[++p];
+ }
+ assert(field == fields_N);
+}
+
+/*
+ * region table
+ */
+
+static int generate_rows_for_region(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val)
+{
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ // not used
+ (void) src_db;
+ (void) src_key;
+ assert(*(uint32_t*)dest_db->app_private == 0);
+
+ // region fields
+ char regionkey[8];
+ char name[32];
+ char comment[160];
+ char row[8+32+160+8];
+ sprintf(row, "%s", (char*)src_val->data);
+
+ const uint32_t fields_N = 3;
+ char *fields[3] = {regionkey, name, comment};
+ tpch_parse_row(row, fields, fields_N);
+
+ if (dest_key->flags==DB_DBT_REALLOC) {
+ if (dest_key->data) toku_free(dest_key->data);
+ dest_key->flags = 0;
+ dest_key->ulen = 0;
+ }
+ if (dest_val->flags==DB_DBT_REALLOC) {
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_val->flags = 0;
+ dest_val->ulen = 0;
+ }
+
+ struct tpch_key *XMALLOC(key);
+ key->orderkey = atoi(regionkey);
+ key->linenumber = atoi(regionkey);
+ key->key = atoi(regionkey);
+
+ char *XMALLOC_N(sizeof(row), val);
+ sprintf(val, "%s|%s", name, comment);
+
+ dbt_init(dest_key, key, sizeof(struct tpch_key));
+ dest_key->flags = DB_DBT_REALLOC;
+
+ dbt_init(dest_val, val, strlen(val)+1);
+ dest_val->flags = DB_DBT_REALLOC;
+
+ return 0;
+}
+
+/*
+ * lineitem table
+ */
+
+
+static int generate_rows_for_lineitem(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val)
+{
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ // not used
+ (void) src_db;
+ (void) src_key;
+
+ // lineitem fields
+ char orderkey[16];
+ char partkey[16];
+ char suppkey[16];
+ char linenumber[8];
+ char quantity[8];
+ char extendedprice[16];
+ char discount[8];
+ char tax[8];
+ char returnflag[8];
+ char linestatus[8];
+ char shipdate[16];
+ char commitdate[16];
+ char receiptdate[16];
+ char shipinstruct[32];
+ char shipmode[16];
+ char comment[48];
+ char row[16+16+16+8+8+16+8+8+8+8+16+16+16+32+16+48 + 8];
+ sprintf(row, "%s", (char*)src_val->data);
+
+ const uint32_t fields_N = 16;
+ char *fields[16] = {orderkey,
+ partkey,
+ suppkey,
+ linenumber,
+ quantity,
+ extendedprice,
+ discount,
+ tax,
+ returnflag,
+ linestatus,
+ shipdate,
+ commitdate,
+ receiptdate,
+ shipinstruct,
+ shipmode,
+ comment};
+ tpch_parse_row(row, fields, fields_N);
+
+ if (dest_key->flags==DB_DBT_REALLOC) {
+ if (dest_key->data) toku_free(dest_key->data);
+ dest_key->flags = 0;
+ dest_key->ulen = 0;
+ }
+ if (dest_val->flags==DB_DBT_REALLOC) {
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_val->flags = 0;
+ dest_val->ulen = 0;
+ }
+
+ struct tpch_key *XMALLOC(key);
+ key->orderkey = atoi(linenumber);
+ key->linenumber = atoi(orderkey);
+
+ char *val;
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ if ( which == 0 ) {
+ val = toku_xstrdup(row);
+ }
+ else {
+ val = toku_xstrdup(orderkey);
+ }
+
+ switch(which) {
+ case 0:
+ key->key = atoi(linenumber);
+ break;
+ case 1:
+ // lineitem_fk1
+ key->key = atoi(orderkey);
+ break;
+ case 2:
+ // lineitem_fk2
+ key->key = atoi(suppkey);
+ break;
+ case 3:
+ // lineitem_fk3
+ key->key = atoi(partkey);// not really, ...
+ break;
+ case 4:
+ // lineitem_fk4
+ key->key = atoi(partkey);
+ break;
+ case 5:
+ // li_shp_dt_idx
+ key->key = atoi(linenumber) + atoi(suppkey); // not really ...
+ break;
+ case 6:
+ key->key = atoi(linenumber) +atoi(partkey); // not really ...
+ break;
+ case 7:
+ // li_rcpt_dt_idx
+ key->key = atoi(suppkey) + atoi(partkey); // not really ...
+ break;
+ default:
+ assert(0);
+ }
+
+ dbt_init(dest_key, key, sizeof(struct tpch_key));
+ dest_key->flags = DB_DBT_REALLOC;
+
+ dbt_init(dest_val, val, strlen(val)+1);
+ dest_val->flags = DB_DBT_REALLOC;
+
+ return 0;
+}
+
+
+static void *expect_poll_void = &expect_poll_void;
+static int poll_count=0;
+static int poll_function (void *extra, float progress) {
+ if (0) {
+ static int did_one=0;
+ static struct timeval start;
+ struct timeval now;
+ gettimeofday(&now, 0);
+ if (!did_one) {
+ start=now;
+ did_one=1;
+ }
+ printf("%6.6f %5.1f%%\n", now.tv_sec - start.tv_sec + 1e-6*(now.tv_usec - start.tv_usec), progress*100);
+ }
+ assert(extra==expect_poll_void);
+ assert(0.0<=progress && progress<=1.0);
+ poll_count++;
+ return 0;
+}
+
+static int test_loader(DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[MAX_DBS];
+ uint32_t dbt_flags[MAX_DBS];
+ for(int i=0;i<MAX_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = DISALLOW_PUTS | COMPRESS; // set with -p option
+
+ FILE *fp;
+ // select which table to loader
+ if ( USE_REGION ) {
+ fp = fopen("./region.tbl", "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s:%d %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ return 1;
+ }
+ assert(fp != NULL);
+ } else {
+ fp = fopen("./lineitem.tbl", "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s:%d %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ return 1;
+ }
+ assert(fp != NULL);
+ }
+
+ // create and initialize loader
+
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ r = loader->set_error_callback(loader, NULL, NULL);
+ CKERR(r);
+ r = loader->set_poll_function(loader, poll_function, expect_poll_void);
+ CKERR(r);
+
+ // using loader->put, put values into DB
+ printf("puts "); fflush(stdout);
+ DBT key, val;
+ int k;
+ char v[MAX_ROW_LEN];
+ char *c;
+ c = tpch_read_row(fp, &k, v);
+ int i = 1;
+ while ( c != NULL ) {
+ v[strlen(v)-1] = '\0'; // remove trailing \n
+ dbt_init(&key, &k, sizeof(int));
+ dbt_init(&val, v, strlen(v)+1);
+ r = loader->put(loader, &key, &val);
+ if (DISALLOW_PUTS) {
+ CKERR2(r, EINVAL);
+ } else {
+ CKERR(r);
+ }
+ if (verbose) { if((i++%10000) == 0){printf("."); fflush(stdout);} }
+ c = tpch_read_row(fp, &k, v);
+ }
+ if(verbose) {printf("\n"); fflush(stdout);}
+ fclose(fp);
+
+ poll_count=0;
+
+ // close the loader
+ printf("closing"); fflush(stdout);
+ r = loader->close(loader);
+ printf(" done\n");
+ CKERR(r);
+
+ if ( DISALLOW_PUTS == 0 ) assert(poll_count>0);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ return 0;
+}
+
+static int run_test(void)
+{
+ int r;
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+ r = env->set_default_bt_compare(env, tpch_dbt_cmp); CKERR(r);
+ // select which TPC-H table to load
+ if ( USE_REGION ) {
+ r = env->set_generate_row_callback_for_put(env, generate_rows_for_region); CKERR(r);
+ NUM_DBS=1;
+ }
+ else {
+ r = env->set_generate_row_callback_for_put(env, generate_rows_for_lineitem); CKERR(r);
+ NUM_DBS=8;
+ }
+
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, envdir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ // -------------------------- //
+ int testr = test_loader(dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+
+ return testr;
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ int r = run_test();
+ return r;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -p -g\n%s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-p")==0) {
+ DISALLOW_PUTS = LOADER_DISALLOW_PUTS;
+ } else if (strcmp(argv[0], "-z")==0) {
+ COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-g")==0) {
+ USE_REGION = 1;
+ } else if (strcmp(argv[0], "-e") == 0) {
+ argc--; argv++;
+ if (argc > 0)
+ envdir = argv[0];
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/locktree_escalation_stalls.cc b/storage/tokudb/PerconaFT/src/tests/locktree_escalation_stalls.cc
new file mode 100644
index 00000000000..b4874472bcb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/locktree_escalation_stalls.cc
@@ -0,0 +1,259 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// This test ensures that lock escalation occurs on the big transaction thread.
+// locktree_escalation_stalls --max_i 1000000000 --n_small 16 --verbose
+
+#include "test.h"
+#include <db.h>
+#include "toku_time.h"
+#include "toku_pthread.h"
+
+// from #include "threaded_stress_test_helpers.h"
+// For each line of engine status output, look for lines that contain substrings
+// that match any of the strings in the pattern string. The pattern string contains
+// 0 or more strings separated by the '|' character, kind of like a regex.
+static void print_matching_engine_status_rows(DB_ENV *env, const char *pattern) {
+ uint64_t num_rows;
+ env->get_engine_status_num_rows(env, &num_rows);
+ uint64_t buf_size = num_rows * 128;
+ const char *row;
+ char *row_r;
+
+ char *pattern_copy = toku_xstrdup(pattern);
+ int num_patterns = 1;
+ for (char *p = pattern_copy; *p != '\0'; p++) {
+ if (*p == '|') {
+ *p = '\0';
+ num_patterns++;
+ }
+ }
+
+ char *XMALLOC_N(buf_size, buf);
+ int r = env->get_engine_status_text(env, buf, buf_size);
+ invariant_zero(r);
+
+ for (row = strtok_r(buf, "\n", &row_r); row != nullptr; row = strtok_r(nullptr, "\n", &row_r)) {
+ const char *p = pattern_copy;
+ for (int i = 0; i < num_patterns; i++, p += strlen(p) + 1) {
+ if (strstr(row, p) != nullptr) {
+ fprintf(stderr, "%s\n", row);
+ }
+ }
+ }
+
+ toku_free(pattern_copy);
+ toku_free(buf);
+ fflush(stderr);
+}
+
+static volatile int killed = 0;
+
+// in a big transaction, insert a bunch of rows.
+static void big_test(DB_ENV *env, DB *db, uint64_t max_i) {
+ if (verbose)
+ fprintf(stderr, "%u %s\n", toku_os_gettid(), __FUNCTION__);
+ int r;
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ assert(r == 0);
+
+ for (uint64_t i = 0; !killed && i < max_i; i++) {
+ uint64_t k = htonl(i);
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &i, .size = sizeof i };
+ uint64_t t_start = toku_current_time_microsec();
+ r = db->put(db, txn, &key, &val, 0);
+ assert(r == 0);
+ uint64_t t_end = toku_current_time_microsec();
+ uint64_t t_delta = t_end - t_start;
+ if (t_delta >= 1000000) {
+ fprintf(stderr, "%u %s i=%" PRIu64 " %" PRIu64 "\n", toku_os_gettid(), __FUNCTION__, i, t_delta);
+ if (verbose)
+ print_matching_engine_status_rows(env, "locktree");
+ }
+
+ toku_pthread_yield();
+ }
+
+ r = txn->commit(txn, 0);
+ assert(r == 0);
+}
+
+// insert a row in a single transaction.
+static void small_test(DB_ENV *env, DB *db, uint64_t max_i) {
+ if (verbose)
+ fprintf(stderr, "%u %s\n", toku_os_gettid(), __FUNCTION__);
+ int r;
+ uint64_t k = toku_os_gettid(); // get a unique number
+ for (uint64_t i = 0; !killed && i < max_i; i++) {
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ assert(r == 0);
+
+ DBT key = { .data = &k, .size = sizeof k };
+ DBT val = { .data = &i, .size = sizeof i };
+ uint64_t t_start = toku_current_time_microsec();
+ r = db->put(db, txn, &key, &val, 0);
+ assert(r == 0);
+ uint64_t t_end = toku_current_time_microsec();
+ uint64_t t_delta = t_end - t_start;
+ if (t_delta >= 1000000) {
+ fprintf(stderr, "%u %s %" PRIu64 "\n", toku_os_gettid(), __FUNCTION__, t_delta);
+ assert(0);
+ }
+
+ r = txn->commit(txn, 0);
+ assert(r == 0);
+
+ toku_pthread_yield();
+ }
+}
+
+struct test_args {
+ DB_ENV *env;
+ DB *db;
+ uint64_t max_i;
+ void (*test_f)(DB_ENV *env, DB *db, uint64_t max_i);
+};
+
+static void *test_f(void *args) {
+ struct test_args *test_args = (struct test_args *) args;
+ test_args->test_f(test_args->env, test_args->db, test_args->max_i);
+ return args;
+}
+
+static void run_test(uint64_t max_i, int n_small) {
+ int r;
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->set_cachesize(env, 8, 0, 1);
+ assert(r == 0);
+ r = env->set_lk_max_memory(env, 1000000000);
+ assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK+DB_INIT_MPOOL+DB_INIT_TXN+DB_INIT_LOG + DB_CREATE + DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB *big_db = NULL;
+ r = db_create(&big_db, env, 0);
+ assert(r == 0);
+
+ r = big_db->open(big_db, NULL, "big", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB *small_db = NULL;
+ r = db_create(&small_db, env, 0);
+ assert(r == 0);
+
+ r = small_db->open(small_db, NULL, "small", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ struct test_args big_test_args = {
+ env, big_db, max_i, big_test,
+ };
+ toku_pthread_t big_id;
+ r = toku_pthread_create(&big_id, NULL, test_f, &big_test_args);
+ assert(r == 0);
+
+ struct test_args small_test_args[n_small];
+ toku_pthread_t small_id[n_small];
+ for (int i = 0; i < n_small; i++) {
+ small_test_args[i] = { env, small_db, max_i, small_test };
+ r = toku_pthread_create(&small_id[i], NULL, test_f, &small_test_args[i]);
+ assert(r == 0);
+ }
+
+ void *big_ret;
+ r = toku_pthread_join(big_id, &big_ret);
+ assert(r == 0);
+
+ killed = 1;
+
+ for (int i = 0; i < n_small; i++) {
+ void *small_ret;
+ r = toku_pthread_join(small_id[i], &small_ret);
+ assert(r == 0);
+ }
+
+ r = small_db->close(small_db, 0);
+ assert(r == 0);
+
+ r = big_db->close(big_db, 0);
+ assert(r == 0);
+
+ r = env->close(env, 0);
+ assert(r == 0);
+}
+
+int test_main (int argc, char * const argv[]) {
+ int r;
+ uint64_t max_i = 10000;
+ int n_small = 1;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0) verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--max_i") == 0 && i+1 < argc) {
+ max_i = atoll(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "--n_small") == 0 && i+1 < argc) {
+ n_small = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ run_test(max_i, n_small);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/manyfiles.cc b/storage/tokudb/PerconaFT/src/tests/manyfiles.cc
new file mode 100644
index 00000000000..c82bf4c3c96
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/manyfiles.cc
@@ -0,0 +1,123 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* How fast can we do insertions when there are many files? */
+
+#include <db.h>
+#include <sys/stat.h>
+
+#define NFILES 1000
+#define NINSERTS_PER 1000
+
+static DB_ENV *env;
+static DB *dbs[NFILES];
+DB_TXN *txn;
+
+static void
+test_setup (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ multiply_locks_for_n_dbs(env, NFILES);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+
+ int i;
+
+ for (i=0; i<NFILES; i++) {
+ char fname[20];
+ snprintf(fname, sizeof(fname), "foo%d.db", i);
+ r=db_create(&dbs[i], env, 0); CKERR(r);
+ r = dbs[i]->set_pagesize(dbs[i], 4096);
+ r=dbs[i]->open(dbs[i], txn, fname, 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ }
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int i;
+ int r;
+ for (i=0; i<NFILES; i++) {
+ r= dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+ r= env->close(env, 0); CKERR(r);
+}
+
+static void
+doit (void) {
+ int j;
+ int r;
+ struct timeval startt, endt;
+ gettimeofday(&startt, 0);
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ for (j=0; j<NINSERTS_PER; j++) {
+ int i;
+ DBT key,data;
+ char str[10];
+ snprintf(str, sizeof(str), "%08d", j);
+ dbt_init(&key, str, 1+strlen(str));
+ dbt_init(&data, str, 1+strlen(str));
+ for (i=0; i<NFILES; i++) {
+ r = dbs[i]->put(dbs[i], txn, &key, &data, 0);
+ CKERR(r);
+ }
+ }
+ r=txn->commit(txn, 0); assert(r==0);
+ gettimeofday(&endt, 0);
+ long long ninserts = NINSERTS_PER * NFILES;
+ double diff = (endt.tv_sec - startt.tv_sec) + 1e-6*(endt.tv_usec-startt.tv_usec);
+ if (verbose) printf("%lld insertions in %9.6fs, %9.3f ins/s \n", ninserts, diff, ninserts/diff);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+
+ test_setup();
+ doit();
+ test_shutdown();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/maxsize-for-loader.cc b/storage/tokudb/PerconaFT/src/tests/maxsize-for-loader.cc
new file mode 100644
index 00000000000..78d91715be8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/maxsize-for-loader.cc
@@ -0,0 +1,392 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "toku_random.h"
+
+bool fast = false;
+
+DB_ENV *env;
+enum {NUM_DBS=2};
+uint32_t USE_COMPRESS=0;
+
+bool do_check = false;
+uint32_t num_rows = 1;
+uint32_t which_db_to_fail = (uint32_t) -1;
+uint32_t which_row_to_fail = (uint32_t) -1;
+enum how_to_fail { FAIL_NONE, FAIL_KSIZE, FAIL_VSIZE } how_to_fail = FAIL_NONE;
+
+static struct random_data random_data[NUM_DBS];
+char random_buf[NUM_DBS][8];
+
+static int put_multiple_generate(DB *dest_db,
+ DB *src_db __attribute__((__unused__)),
+ DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals,
+ const DBT *src_key, const DBT *src_val __attribute__((__unused__))) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+ assert(src_key->size==4);
+ uint32_t rownum = *(uint32_t*)src_key->data;
+
+ uint32_t ksize, vsize;
+ const uint32_t kmax=32*1024, vmax=32*1024*1024;
+ if (which==which_db_to_fail && rownum==which_row_to_fail) {
+ switch (how_to_fail) {
+ case FAIL_NONE: ksize=kmax; vsize=vmax; goto gotsize;
+ case FAIL_KSIZE: ksize=kmax+1; vsize=vmax; goto gotsize;
+ case FAIL_VSIZE: ksize=kmax; vsize=vmax+1; goto gotsize;
+ }
+ assert(0);
+ gotsize:;
+ } else {
+ ksize=4; vsize=100;
+ }
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < ksize) {
+ dest_key->data = toku_xrealloc(dest_key->data, ksize);
+ dest_key->ulen = ksize;
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < vsize) {
+ dest_val->data = toku_xrealloc(dest_val->data, vsize);
+ dest_val->ulen = vsize;
+ }
+ assert(ksize>=sizeof(uint32_t));
+ for (uint32_t i=0; i<ksize; i++) ((char*)dest_key->data)[i] = myrandom_r(&random_data[which]);
+ for (uint32_t i=0; i<vsize; i++) ((char*)dest_val->data)[i] = myrandom_r(&random_data[which]);
+ *(uint32_t*)dest_key->data = rownum;
+ dest_key->size = ksize;
+ dest_val->size = vsize;
+
+ return 0;
+}
+
+struct error_extra {
+ int bad_i;
+ int error_count;
+};
+
+static void error_callback (DB *db __attribute__((__unused__)), int which_db, int err, DBT *key __attribute__((__unused__)), DBT *val __attribute__((__unused__)), void *extra) {
+ struct error_extra *e =(struct error_extra *)extra;
+ assert(which_db==(int)which_db_to_fail);
+ assert(err==EINVAL);
+ assert(e->error_count==0);
+ e->error_count++;
+}
+
+static void reset_random(void) {
+ int r;
+
+ for (int i = 0; i < NUM_DBS; i++) {
+ ZERO_STRUCT(random_data[i]);
+ ZERO_ARRAY(random_buf[i]);
+ r = myinitstate_r(i, random_buf[i], 8, &random_data[i]);
+ assert(r==0);
+ }
+}
+
+static void test_loader_maxsize(DB **dbs, DB **check_dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[NUM_DBS];
+ uint32_t dbt_flags[NUM_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = USE_COMPRESS; // set with -p option
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, nullptr, NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ assert(which_db_to_fail != 0);
+ CKERR(r);
+ struct error_extra error_extra = {.bad_i=0,.error_count=0};
+ r = loader->set_error_callback(loader, error_callback, (void*)&error_extra);
+ CKERR(r);
+ r = loader->set_poll_function(loader, NULL, NULL);
+ CKERR(r);
+
+ reset_random();
+ // using loader->put, put values into DB
+ DBT key, val;
+ unsigned int k, v;
+ for(uint32_t i=0;i<num_rows;i++) {
+ k = i;
+ v = i;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ CKERR(r);
+ }
+
+ // close the loader
+ if (verbose) { printf("closing"); fflush(stdout); }
+ r = loader->close(loader);
+ if (verbose) { printf(" done\n"); }
+ switch(how_to_fail) {
+ case FAIL_NONE: assert(r==0); assert(error_extra.error_count==0); goto checked;
+ case FAIL_KSIZE: assert(r==EINVAL); assert(error_extra.error_count==1); goto checked;
+ case FAIL_VSIZE: assert(r==EINVAL); assert(error_extra.error_count==1); goto checked;
+ }
+ assert(0);
+ checked:
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ if (do_check && how_to_fail==FAIL_NONE) {
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ reset_random();
+ DBT keys[NUM_DBS];
+ DBT vals[NUM_DBS];
+ uint32_t flags[NUM_DBS];
+ for (int i = 0; i < NUM_DBS; i++) {
+ dbt_init_realloc(&keys[i]);
+ dbt_init_realloc(&vals[i]);
+ flags[i] = 0;
+ }
+
+ for(uint32_t i=0;i<num_rows;i++) {
+ k = i;
+ v = i;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = env_put_multiple_test_no_array(env, nullptr, txn, &key, &val, NUM_DBS, check_dbs, keys, vals, flags);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ for (int i = 0; i < NUM_DBS; i++) {
+ DBC *loader_cursor;
+ DBC *check_cursor;
+ r = dbs[i]->cursor(dbs[i], txn, &loader_cursor, 0);
+ CKERR(r);
+ r = dbs[i]->cursor(check_dbs[i], txn, &check_cursor, 0);
+ CKERR(r);
+ DBT loader_key;
+ DBT loader_val;
+ DBT check_key;
+ DBT check_val;
+ dbt_init_realloc(&loader_key);
+ dbt_init_realloc(&loader_val);
+ dbt_init_realloc(&check_key);
+ dbt_init_realloc(&check_val);
+ for (uint32_t x = 0; x <= num_rows; x++) {
+ int r_loader = loader_cursor->c_get(loader_cursor, &loader_key, &loader_val, DB_NEXT);
+ int r_check = check_cursor->c_get(check_cursor, &check_key, &check_val, DB_NEXT);
+ assert(r_loader == r_check);
+ if (x == num_rows) {
+ CKERR2(r_loader, DB_NOTFOUND);
+ CKERR2(r_check, DB_NOTFOUND);
+ } else {
+ CKERR(r_loader);
+ CKERR(r_check);
+ }
+ assert(loader_key.size == check_key.size);
+ assert(loader_val.size == check_val.size);
+ assert(memcmp(loader_key.data, check_key.data, loader_key.size) == 0);
+ assert(memcmp(loader_val.data, check_val.data, loader_val.size) == 0);
+ }
+ toku_free(loader_key.data);
+ toku_free(loader_val.data);
+ toku_free(check_key.data);
+ toku_free(check_val.data);
+ loader_cursor->c_close(loader_cursor);
+ check_cursor->c_close(check_cursor);
+ }
+
+ for (int i = 0; i < NUM_DBS; i++) {
+ toku_free(keys[i].data);
+ toku_free(vals[i].data);
+ dbt_init_realloc(&keys[i]);
+ dbt_init_realloc(&vals[i]);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+
+
+}
+
+char *free_me = NULL;
+const char *env_dir = TOKU_TEST_FILENAME; // the default env_dir
+
+static void create_and_open_dbs(DB **dbs, const char *suffix, int *idx) {
+ int r;
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ enum {MAX_NAME=128};
+ char name[MAX_NAME*2];
+
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x_%s", i, suffix);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+}
+
+static int
+uint_or_size_dbt_cmp (DB *db, const DBT *a, const DBT *b) {
+ assert(db && a && b);
+ if (a->size == sizeof(unsigned int) && b->size == sizeof(unsigned int)) {
+ return uint_dbt_cmp(db, a, b);
+ }
+ return a->size - b->size;
+}
+
+static void run_test(uint32_t nr, uint32_t wdb, uint32_t wrow, enum how_to_fail htf) {
+ num_rows = nr; which_db_to_fail = wdb; which_row_to_fail = wrow; how_to_fail = htf;
+
+ int r;
+ toku_os_recursive_delete(env_dir);
+ r = toku_os_mkdir(env_dir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_or_size_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOG | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DB **XMALLOC_N(NUM_DBS, dbs);
+ DB **XMALLOC_N(NUM_DBS, check_dbs);
+ int idx[NUM_DBS];
+
+ create_and_open_dbs(dbs, "loader", &idx[0]);
+ if (do_check && how_to_fail==FAIL_NONE) {
+ create_and_open_dbs(check_dbs, "check", &idx[0]);
+ }
+
+ if (verbose) printf("running test_loader()\n");
+ // -------------------------- //
+ test_loader_maxsize(dbs, check_dbs);
+ // -------------------------- //
+ if (verbose) printf("done test_loader()\n");
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ if (do_check && how_to_fail==FAIL_NONE) {
+ check_dbs[i]->close(check_dbs[i], 0); CKERR(r);
+ check_dbs[i] = NULL;
+ }
+ }
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+ toku_free(check_dbs);
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int num_rows_set = false;
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+
+ run_test(1, (uint32_t) -1, (uint32_t) -1, FAIL_NONE);
+ run_test(1, 1, 0, FAIL_NONE);
+ run_test(1, 1, 0, FAIL_KSIZE);
+ run_test(1, 1, 0, FAIL_VSIZE);
+ if (!fast) {
+ run_test(1000000, 1, 500000, FAIL_KSIZE);
+ run_test(1000000, 1, 500000, FAIL_VSIZE);
+ }
+ toku_free(free_me);
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: %s [-h] [-v] [-q] [-p] [-f]\n", cmd);
+ fprintf(stderr, " where -e <env> uses <env> to construct the directory (so that different tests can run concurrently)\n");
+ fprintf(stderr, " -h help\n");
+ fprintf(stderr, " -v verbose\n");
+ fprintf(stderr, " -q quiet\n");
+ fprintf(stderr, " -z compress intermediates\n");
+ fprintf(stderr, " -c compare with regular dbs\n");
+ fprintf(stderr, " -f fast (suitable for vgrind)\n");
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-c")==0) {
+ do_check = true;
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-z")==0) {
+ USE_COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ } else if (strcmp(argv[0], "-f")==0) {
+ fast = true;
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/medium-nested-commit-commit.cc b/storage/tokudb/PerconaFT/src/tests/medium-nested-commit-commit.cc
new file mode 100644
index 00000000000..6adfdc93471
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/medium-nested-commit-commit.cc
@@ -0,0 +1,152 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test to see if a medium-size nested transaction (the nested pieces are not too big, but the whole thing is so big that it's rollbacks spill into a file)
+ * can commit properly.
+ * Four Tests:
+ * big child aborts, parent aborts
+ * big child aborts, parent commits
+ * big child commits, parent aborts
+ * big child commits, parent commits (This test)
+ */
+
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *xchild, *xparent;
+
+static void insert (int i) {
+ char hello[30], there[30];
+ DBT key,data;
+ if (verbose) printf("Insert %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ int r = db->put(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void lookup (int i, int expect, int expectj) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ memset(&data, 0, sizeof(data));
+ if (verbose) printf("Looking up %d (expecting %s)\n", i, expect==0 ? "to find" : "not to find");
+ int r = db->get(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &data,
+ 0);
+ assert(expect==r);
+ if (expect==0) {
+ CKERR(r);
+ snprintf(there, sizeof(there), "there%d", expectj);
+ assert(data.size==strlen(there)+1);
+ assert(strcmp((char*)data.data, there)==0);
+ }
+}
+
+const int N = 50000;
+const int DIV = 10;
+
+static void
+test_commit_commit (void) {
+ int i, j, k, r;
+ r=env->txn_begin(env, 0, &xparent, 0); CKERR(r);
+ k=0;
+ for (j=0; j<DIV; j++) {
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N/DIV; i++) {
+ insert(k);
+ k++;
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ }
+ k=0;
+ for (j=0; j<DIV; j++) {
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ for (i=0; i<N/DIV; i++) {
+ lookup(k, 0, k);
+ k++;
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ }
+ r=xparent->commit(xparent, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &xchild, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ lookup(i, 0, i);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+}
+
+static void
+test_setup (void) {
+ DB_TXN *txn;
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void
+test_shutdown (void) {
+ int r;
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ test_setup();
+ test_commit_commit();
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/multiprocess.cc b/storage/tokudb/PerconaFT/src/tests/multiprocess.cc
new file mode 100644
index 00000000000..3c8cd60d947
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/multiprocess.cc
@@ -0,0 +1,234 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+#include "test.h"
+
+static void
+test_env (const char *envdir0, const char *envdir1, int expect_open_return) {
+ int r;
+ toku_os_recursive_delete(envdir0);
+ r = toku_os_mkdir(envdir0, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ if (strcmp(envdir0, envdir1) != 0) {
+ toku_os_recursive_delete(envdir1);
+ r = toku_os_mkdir(envdir1, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ }
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_redzone(env, 0);
+ CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_RECOVER;
+ r = env->open(env, envdir0, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_ENV *env2;
+ r = db_env_create(&env2, 0);
+ CKERR(r);
+ r = env2->set_redzone(env2, 0);
+ CKERR(r);
+ r = env2->open(env2, envdir1, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR2(r, expect_open_return);
+
+ r = env->close(env, 0);
+ CKERR(r);
+
+ if (expect_open_return != 0) {
+ r = env2->open(env2, envdir1, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ }
+
+ r = env2->close(env2, 0);
+ CKERR(r);
+}
+
+static void
+test_datadir (const char *envdir0, const char *datadir0, const char *envdir1, const char *datadir1, int expect_open_return) {
+ char s[256];
+
+ int r;
+ sprintf(s, "rm -rf %s", envdir0);
+ r = system(s);
+ CKERR(r);
+ r = toku_os_mkdir(envdir0, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ sprintf(s, "rm -rf %s", datadir0);
+ r = system(s);
+ CKERR(r);
+ r = toku_os_mkdir(datadir0, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ sprintf(s, "rm -rf %s", envdir1);
+ r = system(s);
+ CKERR(r);
+ r = toku_os_mkdir(envdir1, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ sprintf(s, "rm -rf %s", datadir1);
+ r = system(s);
+ CKERR(r);
+ r = toku_os_mkdir(datadir1, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_redzone(env, 0);
+ CKERR(r);
+ r = env->set_data_dir(env, datadir0);
+ CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_RECOVER;
+ r = env->open(env, envdir0, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_ENV *env2;
+ r = db_env_create(&env2, 0);
+ CKERR(r);
+ r = env2->set_redzone(env2, 0);
+ CKERR(r);
+ r = env2->set_data_dir(env2, datadir1);
+ CKERR(r);
+ r = env2->open(env2, envdir1, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR2(r, expect_open_return);
+
+ r = env->close(env, 0);
+ CKERR(r);
+
+ if (expect_open_return != 0) {
+ r = env2->open(env2, envdir1, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ }
+
+ r = env2->close(env2, 0);
+ CKERR(r);
+}
+static void
+test_logdir (const char *envdir0, const char *datadir0, const char *envdir1, const char *datadir1, int expect_open_return) {
+ char s[256];
+
+ int r;
+ sprintf(s, "rm -rf %s", envdir0);
+ r = system(s);
+ CKERR(r);
+ r = toku_os_mkdir(envdir0, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ sprintf(s, "rm -rf %s", datadir0);
+ r = system(s);
+ CKERR(r);
+ r = toku_os_mkdir(datadir0, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ sprintf(s, "rm -rf %s", envdir1);
+ r = system(s);
+ CKERR(r);
+ r = toku_os_mkdir(envdir1, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ sprintf(s, "rm -rf %s", datadir1);
+ r = system(s);
+ CKERR(r);
+ r = toku_os_mkdir(datadir1, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_redzone(env, 0);
+ CKERR(r);
+ r = env->set_lg_dir(env, datadir0);
+ CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_RECOVER;
+ r = env->open(env, envdir0, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_ENV *env2;
+ r = db_env_create(&env2, 0);
+ CKERR(r);
+ r = env2->set_redzone(env2, 0);
+ CKERR(r);
+ r = env2->set_lg_dir(env2, datadir1);
+ CKERR(r);
+ r = env2->open(env2, envdir1, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR2(r, expect_open_return);
+
+ r = env->close(env, 0);
+ CKERR(r);
+
+ if (expect_open_return != 0) {
+ r = env2->open(env2, envdir1, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ }
+
+ r = env2->close(env2, 0);
+ CKERR(r);
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU|S_IRWXG|S_IRWXO);
+ assert_zero(r);
+
+ char env0[TOKU_PATH_MAX+1];
+ char env1[TOKU_PATH_MAX+1];
+ toku_path_join(env0, 2, TOKU_TEST_FILENAME, "e0");
+ toku_path_join(env1, 2, TOKU_TEST_FILENAME, "e1");
+ test_env(env0, env1, 0);
+ test_env(env0, env0, EWOULDBLOCK);
+ char wd[TOKU_PATH_MAX+1];
+ char *cwd = getcwd(wd, sizeof wd);
+ assert(cwd != nullptr);
+ char data0[TOKU_PATH_MAX+1];
+ toku_path_join(data0, 3, cwd, TOKU_TEST_FILENAME, "d0");
+ char data1[TOKU_PATH_MAX+1];
+ toku_path_join(data1, 3, cwd, TOKU_TEST_FILENAME, "d1");
+ test_datadir(env0, data0, env1, data1, 0);
+ test_datadir(env0, data0, env1, data0, EWOULDBLOCK);
+ test_logdir(env0, data0, env1, data1, 0);
+ test_logdir(env0, data0, env1, data0, EWOULDBLOCK);
+
+ toku_os_recursive_delete(env0);
+ toku_os_recursive_delete(env1);
+ toku_os_recursive_delete(data0);
+ toku_os_recursive_delete(data1);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/mvcc-create-table.cc b/storage/tokudb/PerconaFT/src/tests/mvcc-create-table.cc
new file mode 100644
index 00000000000..5f8becb2993
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/mvcc-create-table.cc
@@ -0,0 +1,88 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that isolation works right for subtransactions.
+// In particular, check to see what happens if a subtransaction has different isolation level from its parent.
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+
+ DB_TXN* txna = NULL;
+ DB_TXN* txnb = NULL;
+ DB_TXN* txnc = NULL;
+ DBC* c;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ DBT key,val;
+ r = db->put(db, txna, dbt_init(&key, "a", 2), dbt_init(&val, "a", 2), 0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txnb, DB_TXN_SNAPSHOT); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnc, DB_READ_COMMITTED); CKERR(r);
+ r = db->cursor(db, txna, &c, 0); CKERR(r);
+ r = c->c_close(c); CKERR(r);
+ c = NULL;
+ r = txna->commit(txna, 0); CKERR(r);
+
+ r = db->cursor(db, txnb, &c, 0); assert(r == TOKUDB_MVCC_DICTIONARY_TOO_NEW);
+ r = db->cursor(db, txnc, &c, 0); assert(r == TOKUDB_MVCC_DICTIONARY_TOO_NEW);
+
+
+ r = txnb->commit(txnb, 0); CKERR(r);
+ r = txnc->commit(txnc, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/mvcc-many-committed.cc b/storage/tokudb/PerconaFT/src/tests/mvcc-many-committed.cc
new file mode 100644
index 00000000000..3a89cb39d78
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/mvcc-many-committed.cc
@@ -0,0 +1,138 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that isolation works right for subtransactions.
+// In particular, check to see what happens if a subtransaction has different isolation level from its parent.
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ uint32_t i = 0;
+ uint32_t num_read_txns = 1000;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+
+ DB_TXN* create_txn;
+ DB_TXN* read_txns[num_read_txns];
+ DB_TXN* read_uncommitted_txn;
+ memset(read_txns, 0, sizeof(read_txns));
+
+ r = env->txn_begin(env, NULL, &create_txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, create_txn, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = create_txn->commit(create_txn, 0); CKERR(r);
+
+ DBT key,val;
+
+ for (i = 0; i < num_read_txns; i++) {
+ DB_TXN* put_txn = NULL;
+ uint32_t data = i;
+ r = env->txn_begin(env, NULL, &put_txn, DB_TXN_SNAPSHOT);
+ CKERR(r);
+ r = db->put(
+ db,
+ put_txn,
+ dbt_init(&key, "a", 2),
+ dbt_init(&val, &data, 4),
+ 0
+ );
+ CKERR(r);
+ r = put_txn->commit(put_txn, 0);
+ CKERR(r);
+ //this should read the above put
+ r = env->txn_begin(env, NULL, &read_txns[i], DB_TXN_SNAPSHOT);
+ CKERR(r);
+
+ }
+
+ for (i = 0; i < num_read_txns; i++) {
+ DBT curr_key, curr_val;
+ memset(&curr_key, 0, sizeof(curr_key));
+ memset(&curr_val, 0, sizeof(curr_val));
+ DBC* snapshot_cursor = NULL;
+ r = db->cursor(db, read_txns[i], &snapshot_cursor, 0); CKERR(r);
+ r = snapshot_cursor->c_get(snapshot_cursor, &curr_key, &curr_val, DB_NEXT); CKERR(r);
+ assert(((char *)(curr_key.data))[0] == 'a');
+ assert((*(uint32_t *)(curr_val.data)) == i);
+ assert(curr_key.size == 2);
+ assert(curr_val.size == 4);
+ snapshot_cursor->c_close(snapshot_cursor);
+ }
+ {
+ DBT curr_key, curr_val;
+ memset(&curr_key, 0, sizeof(curr_key));
+ memset(&curr_val, 0, sizeof(curr_val));
+ r = env->txn_begin(env, NULL, &read_uncommitted_txn, DB_READ_UNCOMMITTED);
+ CKERR(r);
+ DBC* read_uncommitted_cursor = NULL;
+ r = db->cursor(db, read_uncommitted_txn, &read_uncommitted_cursor, 0); CKERR(r);
+ r = read_uncommitted_cursor->c_get(
+ read_uncommitted_cursor,
+ &curr_key,
+ &curr_val,
+ DB_NEXT
+ );
+ CKERR(r);
+ assert(((char *)(curr_key.data))[0] == 'a');
+ assert((*(uint32_t *)(curr_val.data)) == (num_read_txns - 1));
+ assert(curr_key.size == 2);
+ assert(curr_val.size == 4);
+ read_uncommitted_cursor->c_close(read_uncommitted_cursor);
+ }
+ for (i = 0; i < num_read_txns; i++) {
+ r = read_txns[i]->commit(read_txns[i], 0);
+ CKERR(r);
+ }
+ r = read_uncommitted_txn->commit(read_uncommitted_txn, 0);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/mvcc-read-committed.cc b/storage/tokudb/PerconaFT/src/tests/mvcc-read-committed.cc
new file mode 100644
index 00000000000..eb25bda9f82
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/mvcc-read-committed.cc
@@ -0,0 +1,96 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that isolation works right for subtransactions.
+// In particular, check to see what happens if a subtransaction has different isolation level from its parent.
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+
+ DB_TXN* txna = NULL;
+ DB_TXN* txnb = NULL;
+ DB_TXN* txnc = NULL;
+ DB_TXN* txnb_child = NULL;
+ DB_TXN* txnc_child = NULL;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = txna->commit(txna, 0); CKERR(r);
+
+
+
+ r = env->txn_begin(env, NULL, &txnb, DB_TXN_SNAPSHOT); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnc, DB_READ_COMMITTED); CKERR(r);
+
+ DBT key,val;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+ r = db->put(db, txna, dbt_init(&key, "a", 2), dbt_init(&val, "a", 2), 0); CKERR(r);
+ r = txna->commit(txna, 0); CKERR(r);
+
+ // do a simple test to show that DB_TXN_SNAPSHOT and DB_READ_COMMITTED
+ // work differently
+ r = env->txn_begin(env, txnb, &txnb_child, DB_TXN_SNAPSHOT); CKERR(r);
+ r = env->txn_begin(env, txnc, &txnc_child, DB_READ_COMMITTED); CKERR(r);
+ r = db->get(db, txnb_child, &key, &val, 0);
+ CKERR2(r, DB_NOTFOUND);
+ r = db->get(db, txnc_child, &key, &val, 0);
+ CKERR(r);
+
+ r = txnb_child->commit(txnb_child, 0); CKERR(r);
+ r = txnc_child->commit(txnc_child, 0); CKERR(r);
+ r = txnb->commit(txnb, 0); CKERR(r);
+ r = txnc->commit(txnc, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/openlimit17-locktree.cc b/storage/tokudb/PerconaFT/src/tests/openlimit17-locktree.cc
new file mode 100644
index 00000000000..0c5056e30f3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/openlimit17-locktree.cc
@@ -0,0 +1,117 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/resource.h>
+
+// create 200 databases and close them. set the open file limit to 100 and try to open all of them.
+// eventually, the locktree can not clone fractal tree, and the db open fails.
+
+int test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ int r;
+
+ const int N = 200;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB **dbs = new DB *[N];
+ for (int i = 0; i < N; i++) {
+ dbs[i] = NULL;
+ }
+ for (int i = 0; i < N; i++) {
+ r = db_create(&dbs[i], env, 0);
+ assert(r == 0);
+
+ char dbname[32]; sprintf(dbname, "%d.test", i);
+ r = dbs[i]->open(dbs[i], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+ }
+
+ for (int i = 0; i < N; i++) {
+ if (dbs[i]) {
+ r = dbs[i]->close(dbs[i], 0);
+ assert(r == 0);
+ }
+ }
+
+ struct rlimit nofile_limit = { N/2, N/2 };
+ r = setrlimit(RLIMIT_NOFILE, &nofile_limit);
+ // assert(r == 0); // valgrind does not like this
+ if (r != 0) {
+ printf("warning: set nofile limit to %d failed %d %s\n", N, errno, strerror(errno));
+ }
+
+ for (int i = 0; i < N; i++) {
+ dbs[i] = NULL;
+ }
+ bool emfile_happened = false; // should happen since there are less than N unused file descriptors
+ for (int i = 0; i < N; i++) {
+ r = db_create(&dbs[i], env, 0);
+ assert(r == 0);
+
+ char dbname[32]; sprintf(dbname, "%d.test", i);
+ r = dbs[i]->open(dbs[i], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r == EMFILE) {
+ emfile_happened = true;
+ break;
+ }
+ }
+ assert(emfile_happened);
+ for (int i = 0; i < N; i++) {
+ if (dbs[i]) {
+ r = dbs[i]->close(dbs[i], 0);
+ assert(r == 0);
+ }
+ }
+
+ r = env->close(env, 0);
+ assert(r == 0);
+
+ delete [] dbs;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/openlimit17-metafiles.cc b/storage/tokudb/PerconaFT/src/tests/openlimit17-metafiles.cc
new file mode 100644
index 00000000000..2fea1509faf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/openlimit17-metafiles.cc
@@ -0,0 +1,105 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <fcntl.h>
+#include <sys/resource.h>
+
+// try to open the environment with a small number of unused file descriptors
+
+int test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+ r = env->close(env, 0);
+ assert(r == 0);
+
+ struct rlimit nofile_limit;
+ r = getrlimit(RLIMIT_NOFILE, &nofile_limit);
+ assert(r == 0);
+ const int N = 100;
+ nofile_limit.rlim_cur = N;
+ r = setrlimit(RLIMIT_NOFILE, &nofile_limit);
+ assert(r == 0);
+
+ // compute the number of unused file descriptors
+ int fds[N];
+ for (int i = 0; i < N; i++) {
+ fds[i] = -1;
+ }
+ int unused = 0;
+ for (int i = 0; i < N; i++, unused++) {
+ fds[i] = open("/dev/null", O_RDONLY);
+ if (fds[i] == -1)
+ break;
+ }
+ for (int i = 0; i < N; i++) {
+ if (fds[i] != -1) {
+ close(fds[i]);
+ }
+ }
+
+ // try to open the environment with a constrained number of unused file descriptors. the env open should return an error rather than crash.
+ for (int n = N - unused; n < N; n++) {
+ nofile_limit.rlim_cur = n;
+ r = setrlimit(RLIMIT_NOFILE, &nofile_limit);
+ assert(r == 0);
+
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r == 0) {
+ r = env->close(env, 0);
+ assert(r == 0);
+ break;
+ }
+ assert(r == EMFILE);
+ r = env->close(env, 0);
+ assert(r == 0);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/openlimit17.cc b/storage/tokudb/PerconaFT/src/tests/openlimit17.cc
new file mode 100644
index 00000000000..a36551278f2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/openlimit17.cc
@@ -0,0 +1,100 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/resource.h>
+
+// try to open N databases when N > open file limit. should fail gracefully.
+
+int test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ int r;
+
+ const int N = 200;
+
+ struct rlimit nofile_limit = { N, N };
+ r = setrlimit(RLIMIT_NOFILE, &nofile_limit);
+ // assert(r == 0); // valgrind does not like this
+ if (r != 0) {
+ printf("warning: set nofile limit to %d failed %d %s\n", N, errno, strerror(errno));
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB **dbs = new DB *[N];
+ for (int i = 0; i < N; i++) {
+ dbs[i] = NULL;
+ }
+ bool emfile_happened = false; // should happen since there are less than N unused file descriptors
+ for (int i = 0; i < N; i++) {
+ r = db_create(&dbs[i], env, 0);
+ assert(r == 0);
+
+ char dbname[32]; sprintf(dbname, "%d.test", i);
+ r = dbs[i]->open(dbs[i], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r == EMFILE) {
+ emfile_happened = true;
+ break;
+ }
+ assert(r == 0);
+ }
+
+ assert(emfile_happened);
+
+ for (int i = 0; i < N; i++) {
+ if (dbs[i]) {
+ r = dbs[i]->close(dbs[i], 0);
+ assert(r == 0);
+ }
+ }
+
+ r = env->close(env, 0);
+ assert(r == 0);
+
+ delete [] dbs;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_checkpoint_var.cc b/storage/tokudb/PerconaFT/src/tests/perf_checkpoint_var.cc
new file mode 100644
index 00000000000..c7683f7e265
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_checkpoint_var.cc
@@ -0,0 +1,142 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - Grow the dictionary with insertions
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success. It also tests that snapshots
+// work correctly by verifying that table scans sum their vals to 0.
+//
+// This does NOT test:
+// - splits and merges
+// - multiple DBs
+//
+// Variables that are interesting to tweak and run:
+// - small cachetable
+// - number of elements
+//
+
+static int checkpoint_var(DB_TXN *txn, ARG arg, void* operation_extra, void *stats_extra) {
+ int db_index = random() % arg->cli->num_DBs;
+ int r = 0;
+ int val_size = *(int *)operation_extra;
+ DB* db = arg->dbp[db_index];
+ char data[val_size];
+ memset(data, 0, sizeof(data));
+ int i;
+ for (i = 0; i < 10; i++) {
+ // do point queries
+ ptquery_and_maybe_check_op(db, txn, arg, false);
+ }
+ increment_counter(stats_extra, PTQUERIES, i);
+ for (i = 0; i < 20; i++) {
+ // do a random insertion
+ int rand_key = random() % arg->cli->num_elements;
+ DBT key, val;
+ r = db->put(
+ db,
+ txn,
+ dbt_init(&key, &rand_key, sizeof(rand_key)),
+ dbt_init(&val, data, sizeof(data)),
+ 0);
+ if (r != 0) {
+ goto cleanup;
+ }
+ }
+cleanup:
+ increment_counter(stats_extra, PUTS, i);
+ return r;
+}
+
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - some threads constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - some threads doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ int val_size = cli_args->val_size;
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ for (int i = 0; i < num_threads; i++) {
+ myargs[i].operation = checkpoint_var;
+ myargs[i].operation_extra = &val_size;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ args.env_args.checkpointing_period = 30;
+ args.num_DBs = 4;
+ args.num_ptquery_threads = 4;
+ args.crash_on_operation_failure = false;
+ parse_stress_test_args(argc, argv, &args);
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_child_txn.cc b/storage/tokudb/PerconaFT/src/tests/perf_child_txn.cc
new file mode 100644
index 00000000000..5d4c2ccbc56
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_child_txn.cc
@@ -0,0 +1,90 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure the performace of creating and destroying child
+// transactions. Child transactions should have less work associated with them. They
+// are not added to the live root list and they should not be creating their own snapshots.
+// Nevertheless, benchmarks like tpcc and sysbench create many child transactions
+// for each root transaction, and do little work per child transaction
+
+static int create_child_txn(DB_TXN* txn, ARG arg, void* UU(operation_extra), void *UU(stats_extra)) {
+ DB_TXN* child_txn = NULL;
+ DB_ENV* env = arg->env;
+ int r = env->txn_begin(env, txn, &child_txn, arg->txn_flags);
+ CKERR(r);
+ r = child_txn->commit(child_txn, 0);
+ CKERR(r);
+ return 0;
+}
+
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = create_child_txn;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ args.single_txn = true;
+ parse_stress_test_args(argc, argv, &args);
+ args.num_elements = 0;
+ args.num_DBs = 0;
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_cursor_nop.cc b/storage/tokudb/PerconaFT/src/tests/perf_cursor_nop.cc
new file mode 100644
index 00000000000..9e85dfd11b6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_cursor_nop.cc
@@ -0,0 +1,81 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure the throughput of cursor create and close
+// with multiple threads.
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - some threads constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - some threads doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = cursor_create_close_op;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_iibench.cc b/storage/tokudb/PerconaFT/src/tests/perf_iibench.cc
new file mode 100644
index 00000000000..559401c4579
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_iibench.cc
@@ -0,0 +1,453 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <db.h>
+#include <portability/toku_atomic.h>
+
+#include "test.h"
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test tries to emulate iibench at the ydb layer.
+//
+// The schema is simple:
+// 8 byte primary key
+// 8 byte key A
+// 8 byte key B
+// 8 byte key C
+//
+// There's one primary DB for the pk and three secondary DBs.
+//
+// The primary key stores the other columns as the value.
+// The secondary keys have the primary key appended to them.
+//
+
+static const size_t iibench_secondary_key_size = 16;
+
+struct iibench_row {
+ uint64_t pk;
+ int64_t a;
+ int64_t b;
+ int64_t c;
+};
+
+struct iibench_secondary_row {
+ int64_t column;
+ uint64_t pk;
+};
+
+static int64_t hash(uint64_t key) {
+ uint64_t hash = 0;
+ uint8_t *buf = (uint8_t *) &key;
+ for (int i = 0; i < 8; i++) {
+ hash += (((buf[i] + 1) * 17) & 0xFF) << (i * 8);
+ }
+ return hash;
+}
+
+static int64_t iibench_generate_column_by_pk(int64_t pk, int db_idx) {
+ invariant(db_idx > 0);
+ return hash(pk * db_idx);
+}
+
+static void iibench_generate_row(int64_t pk, struct iibench_row *row) {
+ row->a = iibench_generate_column_by_pk(pk, 1);
+ row->b = iibench_generate_column_by_pk(pk, 2);
+ row->c = iibench_generate_column_by_pk(pk, 3);
+}
+
+static void iibench_parse_row(const DBT *key, const DBT *val, struct iibench_row *row) {
+ char *CAST_FROM_VOIDP(val_buf, val->data);
+ invariant(key->size == 8);
+ invariant(val->size == 24);
+ memcpy(&row->pk, key->data, 8);
+ memcpy(&row->a, val_buf + 0, 8);
+ memcpy(&row->b, val_buf + 8, 8);
+ memcpy(&row->c, val_buf + 16, 8);
+}
+
+static void UU() iibench_verify_row(const struct iibench_row *row) {
+ struct iibench_row expected_row;
+ iibench_generate_row(row->pk, &expected_row);
+ invariant(row->a == expected_row.a);
+ invariant(row->b == expected_row.b);
+ invariant(row->c == expected_row.c);
+}
+
+static void iibench_parse_secondary_row(const DBT *key, const DBT *val, struct iibench_secondary_row *row) {
+ char *CAST_FROM_VOIDP(key_buf, key->data);
+ invariant(key->size == iibench_secondary_key_size);
+ invariant(val->size == 0);
+ memcpy(&row->column, key_buf + 0, 8);
+ memcpy(&row->pk, key_buf + 8, 8);
+}
+
+static void UU() iibench_verify_secondary_row(const struct iibench_secondary_row *row, int db_idx) {
+ int64_t expected = iibench_generate_column_by_pk(row->pk, db_idx);
+ invariant(row->column == expected);
+}
+
+static void iibench_fill_key_buf(uint64_t pk, int64_t *buf) {
+ memcpy(&buf[0], &pk, 8);
+}
+
+static void iibench_fill_val_buf(uint64_t pk, int64_t *buf) {
+ struct iibench_row row;
+ iibench_generate_row(pk, &row);
+ memcpy(&buf[0], &row.a, sizeof(row.a));
+ memcpy(&buf[1], &row.b, sizeof(row.b));
+ memcpy(&buf[2], &row.c, sizeof(row.c));
+}
+
+static int iibench_get_db_idx(DB *db) {
+ DESCRIPTOR desc = db->cmp_descriptor;
+ invariant_notnull(desc->dbt.data);
+ invariant(desc->dbt.size == sizeof(int));
+ int db_idx;
+ memcpy(&db_idx, desc->dbt.data, desc->dbt.size);
+ return db_idx;
+}
+
+static void iibench_rangequery_cb(DB *db, const DBT *key, const DBT *val, void *extra) {
+ invariant_null(extra);
+ const int db_idx = iibench_get_db_idx(db);
+ if (db_idx == 0) {
+ struct iibench_row row;
+ iibench_parse_row(key, val, &row);
+ iibench_verify_row(&row);
+ } else {
+ struct iibench_secondary_row row;
+ iibench_parse_secondary_row(key, val, &row);
+ iibench_verify_secondary_row(&row, db_idx);
+ }
+}
+
+struct iibench_put_op_extra {
+ uint64_t autoincrement;
+};
+
+static int UU() iibench_put_op(DB_TXN *txn, ARG arg, void *operation_extra, void *stats_extra) {
+ const int num_dbs = arg->cli->num_DBs;
+ DB **dbs = arg->dbp;
+ DB_ENV *env = arg->env;
+ DBT_ARRAY mult_key_dbt[num_dbs];
+ DBT_ARRAY mult_val_dbt[num_dbs];
+ uint32_t mult_put_flags[num_dbs];
+
+ // The first index is unique with serial autoincrement keys.
+ // The rest are have keys generated with this thread's random data.
+ mult_put_flags[0] = get_put_flags(arg->cli) |
+ // If the table was already created, don't check for uniqueness.
+ (arg->cli->num_elements > 0 ? 0 : DB_NOOVERWRITE);
+ for (int i = 0; i < num_dbs; i++) {
+ toku_dbt_array_init(&mult_key_dbt[i], 1);
+ toku_dbt_array_init(&mult_val_dbt[i], 1);
+ mult_put_flags[i] = get_put_flags(arg->cli);
+ }
+ mult_key_dbt[0].dbts[0].flags = 0;
+ mult_val_dbt[0].dbts[0].flags = 0;
+
+ int r = 0;
+
+ uint64_t puts_to_increment = 0;
+ for (uint32_t i = 0; i < arg->cli->txn_size; ++i) {
+ struct iibench_put_op_extra *CAST_FROM_VOIDP(info, operation_extra);
+
+ // Get a random primary key, generate secondary key columns in valbuf
+ uint64_t pk = toku_sync_fetch_and_add(&info->autoincrement, 1);
+ if (arg->bounded_element_range && arg->cli->num_elements > 0) {
+ pk = pk % arg->cli->num_elements;
+ }
+ int64_t keybuf[1];
+ int64_t valbuf[3];
+ iibench_fill_key_buf(pk, keybuf);
+ iibench_fill_val_buf(pk, valbuf);
+ dbt_init(&mult_key_dbt[0].dbts[0], keybuf, sizeof keybuf);
+ dbt_init(&mult_val_dbt[0].dbts[0], valbuf, sizeof valbuf);
+
+ r = env->put_multiple(
+ env,
+ dbs[0], // source db.
+ txn,
+ &mult_key_dbt[0].dbts[0], // source db key
+ &mult_val_dbt[0].dbts[0], // source db value
+ num_dbs, // total number of dbs
+ dbs, // array of dbs
+ mult_key_dbt, // array of keys
+ mult_val_dbt, // array of values
+ mult_put_flags // array of flags
+ );
+ if (r != 0) {
+ goto cleanup;
+ }
+ puts_to_increment++;
+ if (puts_to_increment == 100) {
+ increment_counter(stats_extra, PUTS, puts_to_increment);
+ puts_to_increment = 0;
+ }
+ }
+
+cleanup:
+ for (int i = 0; i < num_dbs; i++) {
+ toku_dbt_array_destroy(&mult_key_dbt[i]);
+ toku_dbt_array_destroy(&mult_val_dbt[i]);
+ }
+ return r;
+}
+
+static int iibench_generate_row_for_put(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *UU(src_key), const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ invariant(src_db != dest_db);
+ // 8 byte primary key, REALLOC secondary key
+ invariant_notnull(src_key->data);
+ invariant(src_key->size == 8);
+ invariant(dest_key->flags == DB_DBT_REALLOC);
+ // Expand the secondary key data buffer if necessary
+ if (dest_key->size != iibench_secondary_key_size) {
+ dest_key->data = toku_xrealloc(dest_key->data, iibench_secondary_key_size);
+ dest_key->size = iibench_secondary_key_size;
+ }
+
+ // Get the db index from the descriptor. This is a secondary index
+ // so it has to be greater than zero (which would be the pk). Then
+ // grab the appropriate secondary key from the source val, which is
+ // an array of the 3 columns, so we have to subtract 1 from the index.
+ const int db_idx = iibench_get_db_idx(dest_db);
+ int64_t *CAST_FROM_VOIDP(columns, src_val->data);
+ int64_t secondary_key = columns[db_idx - 1];
+
+ // First write down the secondary key, then the primary key (in src_key)
+ int64_t *CAST_FROM_VOIDP(dest_key_buf, dest_key->data);
+ memcpy(&dest_key_buf[0], &secondary_key, sizeof(secondary_key));
+ memcpy(&dest_key_buf[1], src_key->data, src_key->size);
+ dest_val->data = nullptr;
+ dest_val->size = 0;
+ return 0;
+}
+
+// After each DB opens, set the descriptor to store the DB idx value.
+// Close and reopen the DB so we can use db->cmp_descriptor during comparisons.
+static DB *iibench_set_descriptor_after_db_opens(DB_ENV *env, DB *db, int idx, reopen_db_fn reopen, struct cli_args *cli_args) {
+ int r;
+ DBT desc_dbt;
+ desc_dbt.data = &idx;
+ desc_dbt.size = sizeof(idx);
+ desc_dbt.ulen = 0;
+ desc_dbt.flags = 0;
+ r = db->change_descriptor(db, nullptr, &desc_dbt, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ reopen(db, idx, cli_args);
+ return db;
+}
+
+static int iibench_compare_keys(DB *db, const DBT *a, const DBT *b) {
+ const int db_idx = iibench_get_db_idx(db);
+ if (db_idx == 0) {
+ invariant(a->size == 8);
+ invariant(b->size == 8);
+ uint64_t x = *(uint64_t *) a->data;
+ uint64_t y = *(uint64_t *) b->data;
+ if (x < y) {
+ return -1;
+ } else if (x == y) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ invariant(a->size == 16);
+ invariant(b->size == 16);
+ int64_t x = *(int64_t *) a->data;
+ int64_t y = *(int64_t *) b->data;
+ uint64_t pk_x = *(uint64_t *) (((char *) a->data) + 8);
+ uint64_t pk_y = *(uint64_t *) (((char *) b->data) + 8);
+ if (x < y) {
+ return -1;
+ } else if (x == y) {
+ if (pk_x < pk_y) {
+ return -1;
+ } else if (pk_x == pk_y) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+ }
+}
+
+static void iibench_rangequery_db(DB *db, DB_TXN *txn, ARG arg, uint64_t max_pk) {
+ const int limit = arg->cli->range_query_limit;
+
+ int r;
+ DBC *cursor;
+
+ // Get a random key no greater than max pk
+ DBT start_key, end_key;
+ uint64_t start_k = myrandom_r(arg->random_data) % (max_pk + 1);
+ uint64_t end_k = start_k + limit;
+ dbt_init(&start_key, &start_k, 8);
+ dbt_init(&end_key, &end_k, 8);
+
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ r = cursor->c_set_bounds(cursor, &start_key, &end_key, true, 0); CKERR(r);
+ struct rangequery_cb_extra extra = {
+ .rows_read = 0,
+ .limit = limit,
+ .cb = iibench_rangequery_cb,
+ .db = db,
+ .cb_extra = nullptr,
+ };
+ r = cursor->c_getf_set(cursor, 0, &start_key, rangequery_cb, &extra);
+ while (r == 0 && extra.rows_read < extra.limit && run_test) {
+ r = cursor->c_getf_next(cursor, 0, rangequery_cb, &extra);
+ }
+
+ r = cursor->c_close(cursor); CKERR(r);
+}
+
+// Do a range query over the primary index, verifying the contents of the rows
+static int iibench_rangequery_op(DB_TXN *txn, ARG arg, void *operation_extra, void *stats_extra) {
+ struct iibench_put_op_extra *CAST_FROM_VOIDP(info, operation_extra);
+ DB *db = arg->dbp[0];
+
+ // Assume the max PK is the table size. If it isn't specified, do a
+ // safe read of the current autoincrement key from the put thread.
+ uint64_t max_pk = arg->cli->num_elements;
+ if (max_pk == 0) {
+ max_pk = toku_sync_fetch_and_add(&info->autoincrement, 0);
+ }
+ iibench_rangequery_db(db, txn, arg, max_pk);
+ increment_counter(stats_extra, PTQUERIES, 1);
+ return 0;
+}
+
+static int iibench_fill_tables(DB_ENV *env, DB **dbs, struct cli_args *cli_args, bool UU(fill_with_zeroes)) {
+ const int num_dbs = cli_args->num_DBs;
+ int r = 0;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ DB_LOADER *loader;
+ uint32_t db_flags[num_dbs];
+ uint32_t dbt_flags[num_dbs];
+ for (int i = 0; i < num_dbs; i++) {
+ db_flags[i] = DB_PRELOCKED_WRITE;
+ dbt_flags[i] = DB_DBT_REALLOC;
+ }
+
+ r = env->create_loader(env, txn, &loader, dbs[0], num_dbs, dbs, db_flags, dbt_flags, 0); CKERR(r);
+ for (int i = 0; i < cli_args->num_elements; i++) {
+ DBT key, val;
+ uint64_t pk = i;
+ int64_t keybuf[1];
+ int64_t valbuf[3];
+ iibench_fill_key_buf(pk, keybuf);
+ iibench_fill_val_buf(pk, valbuf);
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, valbuf, sizeof valbuf);
+ r = loader->put(loader, &key, &val); CKERR(r);
+ if (verbose && i > 0 && i % 10000 == 0) {
+ report_overall_fill_table_progress(cli_args, 10000);
+ }
+ }
+ r = loader->close(loader); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+ return 0;
+}
+
+static void
+stress_table(DB_ENV* env, DB **dbs, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_put_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+
+ // Put threads do iibench-like inserts with an auto-increment primary key
+ // Query threads do range queries of a certain size, verifying row contents.
+
+ struct iibench_put_op_extra put_extra = {
+ .autoincrement = 0
+ };
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbs, env, cli_args);
+ if (i < cli_args->num_put_threads) {
+ myargs[i].operation = iibench_put_op;
+ myargs[i].operation_extra = &put_extra;
+ } else {
+ myargs[i].operation = iibench_rangequery_op;
+ myargs[i].operation_extra = &put_extra;
+ myargs[i].txn_flags |= DB_TXN_READ_ONLY;
+ myargs[i].sleep_ms = 1000; // 1 second between range queries
+ }
+ }
+ const bool crash_at_end = false;
+ run_workers(myargs, num_threads, cli_args->num_seconds, crash_at_end, cli_args);
+}
+
+int test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ args.num_elements = 0; // want to start with empty DBs
+ // Puts per transaction is configurable. It defaults to 1k.
+ args.txn_size = 1000;
+ // Default to one writer on 4 indexes (pk + 3 secondaries), no readers.
+ args.num_DBs = 4;
+ args.num_put_threads = 1;
+ args.num_ptquery_threads = 0;
+ parse_stress_test_args(argc, argv, &args);
+ // The schema is not configurable. Silently ignore whatever was passed in.
+ args.key_size = 8;
+ args.val_size = 32;
+ // when there are multiple threads, its valid for two of them to
+ // generate the same key and one of them fail with DB_LOCK_NOTGRANTED
+ if (args.num_put_threads > 1) {
+ args.crash_on_operation_failure = false;
+ }
+ args.env_args.generate_put_callback = iibench_generate_row_for_put;
+ after_db_open_hook = iibench_set_descriptor_after_db_opens;
+ fill_tables = iibench_fill_tables;
+ perf_test_main_with_cmp(&args, iibench_compare_keys);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_insert.cc b/storage/tokudb/PerconaFT/src/tests/perf_insert.cc
new file mode 100644
index 00000000000..1355cbaa212
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_insert.cc
@@ -0,0 +1,91 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure the throughput of db->puts
+// with multiple threads.
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_put_threads;
+ struct arg myargs[num_threads];
+ operation_t put_op = (cli_args->serial_insert
+ ? serial_put_op
+ : random_put_op_singledb);
+ struct serial_put_extra spe[num_threads];
+ ZERO_ARRAY(spe);
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = put_op;
+ if (cli_args->serial_insert) {
+ spe[i].current = cli_args->num_elements;
+ myargs[i].operation_extra = &spe[i];
+ }
+ }
+ const bool crash_at_end = false;
+ run_workers(myargs, num_threads, cli_args->num_seconds, crash_at_end, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ args.num_elements = 0; // want to start with empty DBs
+ args.key_size = 8;
+ args.val_size = 8;
+ parse_stress_test_args(argc, argv, &args);
+ // when there are multiple threads, its valid for two of them to
+ // generate the same key and one of them fail with DB_LOCK_NOTGRANTED
+ if (args.num_put_threads > 1) {
+ args.crash_on_operation_failure = false;
+ }
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_malloc_free.cc b/storage/tokudb/PerconaFT/src/tests/perf_malloc_free.cc
new file mode 100644
index 00000000000..b7d7dfdcc8b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_malloc_free.cc
@@ -0,0 +1,81 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#define DONT_DEPRECATE_MALLOC 1
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure the throughput of malloc and free
+// with multiple threads.
+
+static int xmalloc_free_op(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ size_t s = 256;
+ void *p = toku_xmalloc(s);
+ toku_free(p);
+ return 0;
+}
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = xmalloc_free_op;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_nop.cc b/storage/tokudb/PerconaFT/src/tests/perf_nop.cc
new file mode 100644
index 00000000000..4c1dfd60b35
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_nop.cc
@@ -0,0 +1,77 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure the throughput of the test infrastructure executing a nop
+// on multiple threads.
+
+static int UU() nop(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ return 0;
+}
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = nop;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_partitioned_counter.cc b/storage/tokudb/PerconaFT/src/tests/perf_partitioned_counter.cc
new file mode 100644
index 00000000000..52ac408fcc7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_partitioned_counter.cc
@@ -0,0 +1,105 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// Measure the throughput of incrementing a status variable on multiple threads.
+
+struct partitioned_counter {
+ struct {
+ union {
+ uint64_t counter;
+ uint64_t junk[64/8];
+ } c;
+ } b[4096/64];
+
+ void increment() {
+#if HAVE_SCHED_GETCPU
+ int n = sched_getcpu();
+#else
+ int n = random();
+#endif
+ assert(n >= 0);
+ toku_sync_fetch_and_add(&b[ n % (4096/64)].c.counter, 1);
+ }
+
+ uint64_t read() {
+ uint64_t s = 0;
+ for (int i = 0; i < 4096/64; i++)
+ s += b[i].c.counter;
+ return s;
+ }
+};
+
+struct partitioned_counter the_counter;
+
+static int UU() nop(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ the_counter.increment();
+ return 0;
+}
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = nop;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_ptquery.cc b/storage/tokudb/PerconaFT/src/tests/perf_ptquery.cc
new file mode 100644
index 00000000000..274e93f05e4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_ptquery.cc
@@ -0,0 +1,102 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - Grow the dictionary with insertions
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success. It also tests that snapshots
+// work correctly by verifying that table scans sum their vals to 0.
+//
+// This does NOT test:
+// - splits and merges
+// - multiple DBs
+//
+// Variables that are interesting to tweak and run:
+// - small cachetable
+// - number of elements
+//
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - some threads constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - some threads doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = ptquery_op;
+ myargs[i].txn_flags |= DB_TXN_READ_ONLY;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_ptquery2.cc b/storage/tokudb/PerconaFT/src/tests/perf_ptquery2.cc
new file mode 100644
index 00000000000..5157430270d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_ptquery2.cc
@@ -0,0 +1,115 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+
+static int ptquery_op2(DB_TXN *txn, ARG arg, void* operation_extra, void *stats_extra) {
+ int db_index = *(int *)operation_extra;
+ DB* db = arg->dbp[db_index];
+ int r = ptquery_and_maybe_check_op(db, txn, arg, true);
+ increment_counter(stats_extra, PTQUERIES, 1);
+ return r;
+}
+
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - Grow the dictionary with insertions
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success. It also tests that snapshots
+// work correctly by verifying that table scans sum their vals to 0.
+//
+// This does NOT test:
+// - splits and merges
+// - multiple DBs
+//
+// Variables that are interesting to tweak and run:
+// - small cachetable
+// - number of elements
+//
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - some threads constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - some threads doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ int thread_ids[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ thread_ids[i] = i % cli_args->num_DBs;
+ myargs[i].operation = ptquery_op2;
+ myargs[i].operation_extra = &thread_ids[i];
+ myargs[i].txn_flags |= DB_TXN_READ_ONLY;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_rangequery.cc b/storage/tokudb/PerconaFT/src/tests/perf_rangequery.cc
new file mode 100644
index 00000000000..bab6a12af28
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_rangequery.cc
@@ -0,0 +1,71 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = rangequery_op;
+ myargs[i].txn_flags |= DB_TXN_READ_ONLY;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_read_txn.cc b/storage/tokudb/PerconaFT/src/tests/perf_read_txn.cc
new file mode 100644
index 00000000000..0a17c0ed411
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_read_txn.cc
@@ -0,0 +1,84 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure the throughput of creating and destroying
+// root read-only transactions that create snapshots
+
+static int UU() nop(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ return 0;
+}
+
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].txn_flags |= DB_TXN_READ_ONLY;
+ myargs[i].operation = nop;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ args.single_txn = false;
+ args.num_elements = 0;
+ args.num_DBs = 0;
+ args.num_put_threads = 0;
+ args.num_update_threads = 0;
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_read_txn_single_thread.cc b/storage/tokudb/PerconaFT/src/tests/perf_read_txn_single_thread.cc
new file mode 100644
index 00000000000..374559d8d31
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_read_txn_single_thread.cc
@@ -0,0 +1,110 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure how fast a single thread can
+// commit and create transactions when there exist N transactions.
+
+DB_TXN** txns;
+int num_txns;
+
+static int commit_and_create_txn(
+ DB_TXN* UU(txn),
+ ARG arg,
+ void* UU(operation_extra),
+ void* UU(stats_extra)
+ )
+{
+ int rand_txn_id = random() % num_txns;
+ int r = txns[rand_txn_id]->commit(txns[rand_txn_id], 0);
+ CKERR(r);
+ r = arg->env->txn_begin(arg->env, 0, &txns[rand_txn_id], arg->txn_flags | DB_TXN_READ_ONLY);
+ CKERR(r);
+ return 0;
+}
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting running of stress\n");
+
+ num_txns = cli_args->txn_size;
+ XCALLOC_N(num_txns, txns);
+ for (int i = 0; i < num_txns; i++) {
+ int r = env->txn_begin(env, 0, &txns[i], DB_TXN_SNAPSHOT);
+ CKERR(r);
+ }
+
+ struct arg myarg;
+ arg_init(&myarg, dbp, env, cli_args);
+ myarg.operation = commit_and_create_txn;
+
+ run_workers(&myarg, 1, cli_args->num_seconds, false, cli_args);
+
+ for (int i = 0; i < num_txns; i++) {
+ int chk_r = txns[i]->commit(txns[i], 0);
+ CKERR(chk_r);
+ }
+ toku_free(txns);
+ num_txns = 0;
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ num_txns = 0;
+ txns = NULL;
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ args.single_txn = true;
+ // this test is all about transactions, make the DB small
+ args.num_elements = 1;
+ args.num_DBs= 1;
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_read_write.cc b/storage/tokudb/PerconaFT/src/tests/perf_read_write.cc
new file mode 100644
index 00000000000..31bb2976fe9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_read_write.cc
@@ -0,0 +1,117 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+static int perf_read(DB_TXN *txn, ARG arg, void* operation_extra, void *stats_extra) {
+ int db_index = *(int *)operation_extra;
+ DB* db = arg->dbp[db_index];
+
+ for (uint32_t i = 0; i < arg->cli->txn_size; i++) {
+ ptquery_and_maybe_check_op(db, txn, arg, true);
+ increment_counter(stats_extra, PTQUERIES, 1);
+ }
+ return 0;
+}
+
+static int perf_write(DB_TXN *txn, ARG arg, void* operation_extra, void *stats_extra) {
+ int db_index = *(int *)operation_extra;
+ DB* db = arg->dbp[db_index];
+ return random_put_in_db(db, txn, arg, true, stats_extra);
+}
+
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - some threads constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - some threads doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads + cli_args->num_update_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+
+ const int num_update_threads = cli_args->num_update_threads;
+ int upd_thread_ids[num_update_threads];
+ for (int i = 0; i < cli_args->num_update_threads; ++i) {
+ upd_thread_ids[i] = i % cli_args->num_DBs;
+ myargs[i].operation_extra = &upd_thread_ids[i];
+ myargs[i].operation = perf_write;
+ }
+
+ const int num_ptquery_threads = cli_args->num_ptquery_threads;
+ int ptq_thread_ids[num_ptquery_threads];
+ for (int i = cli_args->num_update_threads; i < num_threads; i++) {
+ ptq_thread_ids[i] = i % cli_args->num_DBs;
+ myargs[i].operation_extra = &ptq_thread_ids[i];
+ myargs[i].operation = perf_read;
+ }
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ args.env_args.checkpointing_period = 30;
+ args.num_DBs = 1;
+ args.num_ptquery_threads = 1;
+ args.num_update_threads = 1;
+ args.crash_on_operation_failure = false;
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_root_txn.cc b/storage/tokudb/PerconaFT/src/tests/perf_root_txn.cc
new file mode 100644
index 00000000000..d0b35e134f7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_root_txn.cc
@@ -0,0 +1,83 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure the throughput of creating and destroying
+// root read-only transactions that create snapshots
+
+static int UU() nop(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ return 0;
+}
+
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = nop;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ args.single_txn = false;
+ args.num_elements = 0;
+ args.num_DBs = 0;
+ args.num_put_threads = 0;
+ args.num_update_threads = 0;
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_simple_counter.cc b/storage/tokudb/PerconaFT/src/tests/perf_simple_counter.cc
new file mode 100644
index 00000000000..3f5686d5e76
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_simple_counter.cc
@@ -0,0 +1,79 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// Measure the throughput of incrementing a status variable on multiple threads.
+
+volatile uint64_t the_counter = 0;
+
+static int UU() nop(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ toku_sync_fetch_and_add(&the_counter, 1);
+ return 0;
+}
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = nop;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_thread_counter.cc b/storage/tokudb/PerconaFT/src/tests/perf_thread_counter.cc
new file mode 100644
index 00000000000..1897c5fad34
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_thread_counter.cc
@@ -0,0 +1,79 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// Measure the throughput of incrementing a status variable on multiple threads.
+
+volatile __thread uint64_t the_counter = 0;
+
+static int UU() nop(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ the_counter++; // toku_sync_fetch_and_add(&the_counter, 1);
+ return 0;
+}
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = nop;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/perf_txn_single_thread.cc b/storage/tokudb/PerconaFT/src/tests/perf_txn_single_thread.cc
new file mode 100644
index 00000000000..4e0ffc39749
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/perf_txn_single_thread.cc
@@ -0,0 +1,110 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure how fast a single thread can
+// commit and create transactions when there exist N transactions.
+
+DB_TXN** txns;
+int num_txns;
+
+static int commit_and_create_txn(
+ DB_TXN* UU(txn),
+ ARG arg,
+ void* UU(operation_extra),
+ void* UU(stats_extra)
+ )
+{
+ int rand_txn_id = random() % num_txns;
+ int r = txns[rand_txn_id]->commit(txns[rand_txn_id], 0);
+ CKERR(r);
+ r = arg->env->txn_begin(arg->env, 0, &txns[rand_txn_id], arg->txn_flags);
+ CKERR(r);
+ return 0;
+}
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting running of stress\n");
+
+ num_txns = cli_args->txn_size;
+ XCALLOC_N(num_txns, txns);
+ for (int i = 0; i < num_txns; i++) {
+ int r = env->txn_begin(env, 0, &txns[i], DB_TXN_SNAPSHOT);
+ CKERR(r);
+ }
+
+ struct arg myarg;
+ arg_init(&myarg, dbp, env, cli_args);
+ myarg.operation = commit_and_create_txn;
+
+ run_workers(&myarg, 1, cli_args->num_seconds, false, cli_args);
+
+ for (int i = 0; i < num_txns; i++) {
+ int chk_r = txns[i]->commit(txns[i], 0);
+ CKERR(chk_r);
+ }
+ toku_free(txns);
+ num_txns = 0;
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ num_txns = 0;
+ txns = NULL;
+ struct cli_args args = get_default_args_for_perf();
+ parse_stress_test_args(argc, argv, &args);
+ args.single_txn = true;
+ // this test is all about transactions, make the DB small
+ args.num_elements = 1;
+ args.num_DBs= 1;
+ perf_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/powerfail.cc b/storage/tokudb/PerconaFT/src/tests/powerfail.cc
new file mode 100644
index 00000000000..448c7a0ea31
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/powerfail.cc
@@ -0,0 +1,186 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* a powerfail test. */
+#include "test.h"
+#include <stdio.h>
+
+static void print_usage (void) {
+ printf("Two modes:\n");
+ printf(" ./powerfail --write\n");
+ printf(" creates a database, and writes numbers out to stdout. While this is running you can crash the machine and record\n");
+ printf(" the last number printed. It may be helpful to run this program via ssh so that you can see the output after the\n");
+ printf(" machine crashes. It would be wrong to pipe stdout into a file on the machine that crashes, since if we think there\n");
+ printf(" is any possibility that recovery will fail, then the system cannot be trusted to restore that file properly either.\n");
+ printf(" ./powerfail --check N\n");
+ printf(" Feed the recorded number into the command line. The system will check that transaction N committed properly and\n");
+ printf(" that no more than one additional transaction committed.\n");
+}
+
+
+DB_ENV *env;
+enum { N_DBS = 10 };
+DB *dbs[N_DBS];
+char dbname_template[]="foo%d.db";
+const int envflags = DB_INIT_MPOOL|DB_THREAD|DB_CREATE |DB_INIT_LOCK|DB_PRIVATE | DB_INIT_LOG|DB_INIT_TXN|DB_RECOVER;
+
+static void put (DB *db, DB_TXN *txn, long k, long v, int flags) {
+ DBT key, val;
+ int r = db->put(db, txn, dbt_init(&key, &k, sizeof(k)), dbt_init(&val, &v, sizeof(k)), flags);
+ assert(r==0);
+}
+
+static void open_dbs (void) {
+ int r;
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<N_DBS; i++) {
+ char dbname[sizeof(dbname_template)+10];
+ r = snprintf(dbname, sizeof(dbname), dbname_template, i);
+ assert(r>0 && r<(int)sizeof(dbname));
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ r = dbs[i]->open(dbs[i], txn, dbname, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+static void close_dbs (void) {
+ for (int i=0; i<N_DBS; i++) {
+ int r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+
+}
+
+static long shuffle (long l, int i) {
+ (void)i;
+ return l;
+}
+
+static void do_write (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ open_dbs();
+ // DB[0] contains the pairs TXN TXN
+ // A transaction inserts a bunch of records where the vals all add up to 1.
+ for (long N=0; 1; N++) {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ put(dbs[0], txn, N, N, 0);
+ int sum=0;
+ for (int i=1; i+1<N_DBS; i++) {
+ int rval = (random()%2048)-1024;
+ sum+=rval;
+ put(dbs[i], txn, shuffle(N, i), rval, (i%2==0) ? 0 : 0); // even numbered databases are overwritten
+ }
+ put(dbs[N_DBS-1],txn, N, sum, 0);
+ r = txn->commit(txn, 0); CKERR(r);
+ printf("%ld\n", N);
+ }
+}
+
+static void scan(DB *db, DB_TXN *txn,
+ void (*reduce)(DBT *k, DBT *v, void *extra), void *extra) {
+ DBC *cursor;
+ int r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ DBT key, val;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+ key.flags = DB_DBT_MALLOC;
+ val.flags = DB_DBT_MALLOC;
+ int n=0;
+ while (0==(r = cursor->c_get(cursor, &key, &val, DB_NEXT))) {
+ reduce(&key, &val, extra);
+ n++;
+ }
+ printf("n=%d\n", n);
+ r = cursor->c_close(cursor); CKERR(r);
+ toku_free(key.data);
+ toku_free(val.data);
+}
+
+static long maxl (long a, long b) {
+ if (a<b) return b; else return a;
+}
+
+static void maxf (DBT *k, DBT *v, void *extrav) {
+ long *CAST_FROM_VOIDP(extra, extrav);
+ long *CAST_FROM_VOIDP(kd, k->data);
+ long *CAST_FROM_VOIDP(vd, v->data);
+ extra[0] = maxl(extra[0], *kd);
+ extra[1] = maxl(extra[0], *vd);
+}
+
+static void do_check (long N) {
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ open_dbs();
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ long maxp[2]={0,0};
+ scan(dbs[0], txn, maxf, &maxp);
+ printf("max k,v = %ld, %ld\n", maxp[0], maxp[1]);
+ assert(maxp[0]==maxp[1]);
+ assert(maxp[0]>=N);
+ r = txn->commit(txn, 0); CKERR(r);
+ close_dbs();
+ r = env->close(env, 0); CKERR(r);
+
+}
+
+int test_main (int argc, char * const argv[]) {
+ assert(argc>=2 && argc<=3);
+ if (strcmp(argv[1], "--write")==0) {
+ assert(argc==2);
+ do_write();
+ } else if (strcmp(argv[1], "--check")==0) {
+ assert(argc==3);
+ char *end;
+ errno=0;
+ long N=strtol(argv[2], &end, 10);
+ assert(0==errno && 0==*end);
+ do_check(N);
+ } else if (strcmp(argv[1],"-h")==0) {
+ print_usage();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/preload-db-nested.cc b/storage/tokudb/PerconaFT/src/tests/preload-db-nested.cc
new file mode 100644
index 00000000000..5d47b1a8aa7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/preload-db-nested.cc
@@ -0,0 +1,340 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/*********************
+ *
+ * Purpose is to preload a set of dictionaries using nested transactions,
+ * to be used to test version upgrade.
+ *
+ * Each row will be inserted using nested transactions MAXDEPTH deep.
+ * Each nested transaction will insert a value one greater than the parent transaction.
+ * For each row, a single transaction will be aborted, the rest will be committed.
+ * The transaction to be aborted will be the row number mod MAXDEPTH.
+ * So, for row 0, the outermost transaction will be aborted and the row will not appear in the database.
+ * For row 1, transaction 1 will be aborted, so the inserted value will be the original generated value.
+ * For each row, the inserted value will be:
+ * if row%MAXDEPTH == 0 no row
+ * else value = generated value + (row%MAXDEPTH -1)
+ *
+ *
+ * For each row
+ * generate k,v pair
+ * for txndepth = 0 to MAXDEPTH-1 {
+ * add txndepth to v
+ * begin txn
+ * insert
+ * if txndepth = row%MAXDEPTH abort
+ * else commit
+ * }
+ * }
+ *
+ */
+
+
+
+
+
+#define kv_pair_funcs 1 // pull in kv_pair generators from test.h
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+#include "test_kv_gen.h"
+/*
+ */
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+enum {ROWS_PER_TRANSACTION=10000};
+uint NUM_DBS=1;
+uint NUM_ROWS=100000;
+int CHECK_RESULTS=0;
+int optimize=0;
+int littlenode = 0;
+enum { old_default_cachesize=1024 }; // MB
+int CACHESIZE=old_default_cachesize;
+int ALLOW_DUPS=0;
+
+// max depth of nested transactions for this test
+//#define MAXDEPTH 128
+#define MAXDEPTH 64
+
+static void
+nested_insert(DB ** dbs, uint depth, DB_TXN *parent_txn, uint k, uint generated_value);
+
+
+static void
+check_results_nested(DB ** dbs, const uint num_rows) {
+ int num_dbs = 1; // maybe someday increase
+ for(int j=0;j<num_dbs;j++){
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ int r;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+ for(uint i=0;i<num_rows;i++) {
+ if (i % MAXDEPTH) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ CKERR(r);
+ uint observed_k = *(unsigned int*)key.data;
+ uint observed_v = *(unsigned int*)val.data;
+ uint expected_k = i;
+ uint generated_value = generate_val(i, 0);
+ uint expected_v = generated_value + (i%MAXDEPTH - 1);
+ if (verbose >= 3)
+ printf("expected key %d, observed key %d, expected val %d, observed val %d\n",
+ expected_k, observed_k, expected_v, observed_v);
+ // test that we have the expected keys and values
+ assert(observed_k == expected_k);
+ assert(observed_v == expected_v);
+ }
+ dbt_init(&key, NULL, sizeof(unsigned int));
+ dbt_init(&val, NULL, sizeof(unsigned int));
+ if ( verbose && (i%10000 == 0)) {printf("."); fflush(stdout);}
+ }
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_NOSYNC);
+ CKERR(r);
+ }
+ if ( verbose ) {printf("ok");fflush(stdout);}
+}
+
+
+
+
+
+
+static struct timeval starttime;
+static double UU() elapsed_time (void) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return now.tv_sec - starttime.tv_sec + 1e-6*(now.tv_usec - starttime.tv_usec);
+}
+
+static void preload_dbs(DB **dbs)
+{
+ gettimeofday(&starttime, NULL);
+ uint row;
+
+ if ( verbose ) { printf("loading");fflush(stdout); }
+
+ for(row = 0; row <= NUM_ROWS; row++) {
+ uint generated_value = generate_val(row, 0);
+ nested_insert(dbs, 0, NULL, row, generated_value);
+ }
+
+ if (optimize) {
+ if (verbose) { printf("\noptimizing");fflush(stdout);}
+ do_hot_optimize_on_dbs(env, dbs, 1);
+ }
+
+ if ( CHECK_RESULTS) {
+ if ( verbose ) {printf("\nchecking");fflush(stdout);}
+ check_results_nested(&dbs[0], NUM_ROWS);
+ }
+ if ( verbose) {printf("\ndone\n");fflush(stdout);}
+}
+
+static void
+nested_insert(DB ** dbs, uint depth, DB_TXN *parent_txn, uint k, uint generated_value) {
+ if (depth < MAXDEPTH) {
+ DBT key, val;
+ dbt_init_realloc(&key);
+ dbt_init_realloc(&val);
+ uint v = generated_value + depth;
+ DB_TXN * txn;
+ int r = env->txn_begin(env, parent_txn, &txn, 0);
+ CKERR(r);
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ int db = 0; // maybe later replace with loop
+ r = dbs[db]->put(dbs[db], txn, &key, &val, 0);
+ CKERR(r);
+ if (key.flags == 0) { dbt_init_realloc(&key); }
+ if (val.flags == 0) { dbt_init_realloc(&val); }
+ nested_insert(dbs, depth+1, txn, k, generated_value);
+ if (depth == (k % MAXDEPTH)) {
+ r = txn->abort(txn);
+ CKERR(r);
+ if (verbose>=3)
+ printf("abort k = %d, v= %d, depth = %d\n", k, v, depth);
+ }
+ else {
+ r = txn->commit(txn, DB_TXN_NOSYNC);
+ CKERR(r);
+ if (verbose>=3)
+ printf("commit k = %d, v= %d, depth = %d\n", k, v, depth);
+ }
+ if ( verbose && (k%10000 == 0)) {printf(".");fflush(stdout);}
+
+ if ( key.flags ) { toku_free(key.data); key.data = NULL; }
+ if ( val.flags ) { toku_free(val.data); key.data = NULL; }
+ }
+}
+
+
+char *free_me = NULL;
+const char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+
+static void run_test(void)
+{
+ int r;
+ {
+ int len = strlen(env_dir) + 20;
+ char syscmd[len];
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd); CKERR(r);
+ }
+ r = toku_os_mkdir(env_dir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+// r = env->set_default_dup_compare(env, uint_dbt_cmp); CKERR(r);
+// if ( verbose ) printf("CACHESIZE = %d MB\n", CACHESIZE);
+// r = env->set_cachesize(env, CACHESIZE / 1024, (CACHESIZE % 1024)*1024*1024, 1); CKERR(r);
+// CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(uint i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ if (littlenode) {
+ r=dbs[i]->set_pagesize(dbs[i], 4096);
+ CKERR(0);
+ }
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ generate_permute_tables();
+
+ // -------------------------- //
+ preload_dbs(dbs);
+ // -------------------------- //
+
+ for(uint i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const argv[]) {
+ do_args(argc, argv);
+ run_test();
+ if (free_me) toku_free(free_me);
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -c -n -d <num_dbs> -r <num_rows> %s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-c")==0) {
+ CHECK_RESULTS = 1;
+ } else if (strcmp(argv[0], "-n")==0) {
+ littlenode = 1;
+ } else if (strcmp(argv[0], "-o")==0) {
+ optimize = 1;
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/preload-db.cc b/storage/tokudb/PerconaFT/src/tests/preload-db.cc
new file mode 100644
index 00000000000..d06d48a8dec
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/preload-db.cc
@@ -0,0 +1,246 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#define kv_pair_funcs 1 // pull in kv_pair generators from test.h
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+#include "test_kv_gen.h"
+/*
+ */
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+enum {ROWS_PER_TRANSACTION=10000};
+int NUM_DBS=5;
+int NUM_ROWS=100000;
+int CHECK_RESULTS=0;
+int optimize=0;
+int littlenode = 0;
+enum { old_default_cachesize=1024 }; // MB
+int CACHESIZE=old_default_cachesize;
+int ALLOW_DUPS=0;
+
+static struct timeval starttime;
+static double UU() elapsed_time (void) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return now.tv_sec - starttime.tv_sec + 1e-6*(now.tv_usec - starttime.tv_usec);
+}
+
+static void preload_dbs(DB **dbs)
+{
+ gettimeofday(&starttime, NULL);
+ int r;
+ DB_TXN *txn;
+
+ DBT skey, sval;
+ DBT key, val;
+ dbt_init_realloc(&key);
+ dbt_init_realloc(&val);
+ unsigned int k, v;
+ if ( verbose ) { printf("loading");fflush(stdout); }
+ int outer_loop_num = ( NUM_ROWS <= ROWS_PER_TRANSACTION ) ? 1 : (NUM_ROWS / ROWS_PER_TRANSACTION);
+ for(int x=0;x<outer_loop_num;x++) {
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ for(int i=1;i<=ROWS_PER_TRANSACTION;i++) {
+ k = i + (x*ROWS_PER_TRANSACTION);
+ v = generate_val(k, 0);
+ dbt_init(&skey, &k, sizeof(unsigned int));
+ dbt_init(&sval, &v, sizeof(unsigned int));
+
+ for(int db = 0;db < NUM_DBS;db++) {
+ put_multiple_generate(dbs[db], // dest_db
+ NULL, // src_db, ignored
+ &key, &val, // dest_key, dest_val
+ &skey, &sval, // src_key, src_val
+ NULL); // extra, ignored
+
+ r = dbs[db]->put(dbs[db], txn, &key, &val, 0); CKERR(r);
+ if (key.flags == 0) { dbt_init_realloc(&key); }
+ if (val.flags == 0) { dbt_init_realloc(&val); }
+ }
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+ if ( verbose ) {printf(".");fflush(stdout);}
+ }
+ if ( key.flags ) { toku_free(key.data); key.data = NULL; }
+ if ( val.flags ) { toku_free(val.data); key.data = NULL; }
+
+ if (optimize) {
+ if (verbose) { printf("\noptimizing");fflush(stdout);}
+ do_hot_optimize_on_dbs(env, dbs, NUM_DBS);
+ }
+
+ if ( CHECK_RESULTS) {
+ if ( verbose ) {printf("\nchecking");fflush(stdout);}
+ check_results(env, dbs, NUM_DBS, NUM_ROWS);
+ }
+ if ( verbose) {printf("\ndone\n");fflush(stdout);}
+}
+
+
+char *free_me = NULL;
+const char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+
+static void run_test(void)
+{
+ int r;
+ {
+ int len = strlen(env_dir) + 20;
+ char syscmd[len];
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd); CKERR(r);
+ }
+ r = toku_os_mkdir(env_dir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+// r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+// r = env->set_default_dup_compare(env, uint_dbt_cmp); CKERR(r);
+// if ( verbose ) printf("CACHESIZE = %d MB\n", CACHESIZE);
+// r = env->set_cachesize(env, CACHESIZE / 1024, (CACHESIZE % 1024)*1024*1024, 1); CKERR(r);
+// CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ if (littlenode) {
+ r=dbs[i]->set_pagesize(dbs[i], 4096);
+ CKERR(0);
+ }
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ generate_permute_tables();
+
+ // -------------------------- //
+ preload_dbs(dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+
+ /*********** DO NOT TRIM LOGFILES: Trimming logfiles defeats purpose of upgrade tests which must handle untrimmed logfiles.
+ // reopen, then close environment to trim logfiles
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ ***********/
+
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const argv[]) {
+ do_args(argc, argv);
+ run_test();
+ if (free_me) toku_free(free_me);
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -c -n -d <num_dbs> -r <num_rows> %s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-c")==0) {
+ CHECK_RESULTS = 1;
+ } else if (strcmp(argv[0], "-n")==0) {
+ littlenode = 1;
+ } else if (strcmp(argv[0], "-o")==0) {
+ optimize = 1;
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/prelock-read-read.cc b/storage/tokudb/PerconaFT/src/tests/prelock-read-read.cc
new file mode 100644
index 00000000000..100f3af5f45
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/prelock-read-read.cc
@@ -0,0 +1,112 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that prelocking read ranges on multiple transactions do not conflict
+
+static int prelock_range(DBC *cursor, int left, int right) {
+ DBT key_left; dbt_init(&key_left, &left, sizeof left);
+ DBT key_right; dbt_init(&key_right, &right, sizeof right);
+ int r = cursor->c_set_bounds(cursor, &key_left, &key_right, true, 0);
+ return r;
+}
+
+static void test_read_read(DB_ENV *env, DB *db, uint32_t iso_flags, int expect_r) {
+ int r;
+
+ DB_TXN *txn_a = NULL;
+ r = env->txn_begin(env, NULL, &txn_a, iso_flags); assert_zero(r);
+ DB_TXN *txn_b = NULL;
+ r = env->txn_begin(env, NULL, &txn_b, iso_flags); assert_zero(r);
+
+ DBC *cursor_a = NULL;
+ r = db->cursor(db, txn_a, &cursor_a, 0); assert_zero(r);
+ DBC *cursor_b = NULL;
+ r = db->cursor(db, txn_b, &cursor_b, 0); assert_zero(r);
+
+ r = prelock_range(cursor_a, htonl(10), htonl(100)); assert_zero(r);
+ r = prelock_range(cursor_b, htonl(50), htonl(200)); assert(r == expect_r);
+
+ r = cursor_a->c_close(cursor_a); assert_zero(r);
+ r = cursor_b->c_close(cursor_b); assert_zero(r);
+
+ r = txn_a->commit(txn_a, 0); assert_zero(r);
+ r = txn_b->commit(txn_b, 0); assert_zero(r);
+}
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+
+ const char *env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "prelocktest";
+
+ parse_args(argc, argv);
+
+ char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
+ r = system(rm_cmd); assert_zero(r);
+
+ r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ r = env->open(env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+ DB_TXN *create_txn = NULL;
+ r = env->txn_begin(env, NULL, &create_txn, 0); assert_zero(r);
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+ r = create_txn->commit(create_txn, 0); assert_zero(r);
+
+ test_read_read(env, db, DB_READ_COMMITTED, 0);
+ test_read_read(env, db, DB_READ_UNCOMMITTED, 0);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ test_read_read(env, db, DB_SERIALIZABLE, DB_LOCK_NOTGRANTED);
+#else
+ test_read_read(env, db, DB_SERIALIZABLE, 0);
+#endif
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/prelock-read-write.cc b/storage/tokudb/PerconaFT/src/tests/prelock-read-write.cc
new file mode 100644
index 00000000000..a573eeaf05e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/prelock-read-write.cc
@@ -0,0 +1,106 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that prelocking a write range that overlapping a read lock conflicts
+
+static int prelock_range(DBC *cursor, int left, int right) {
+ DBT key_left; dbt_init(&key_left, &left, sizeof left);
+ DBT key_right; dbt_init(&key_right, &right, sizeof right);
+ int r = cursor->c_set_bounds(cursor, &key_left, &key_right, true, 0);
+ return r;
+}
+
+static void test_read_write(DB_ENV *env, DB *db, uint32_t iso_flags, int expect_r) {
+ int r;
+
+ DB_TXN *txn_a = NULL;
+ r = env->txn_begin(env, NULL, &txn_a, iso_flags); assert_zero(r);
+ DB_TXN *txn_b = NULL;
+ r = env->txn_begin(env, NULL, &txn_b, iso_flags); assert_zero(r);
+
+ DBC *cursor_a = NULL;
+ r = db->cursor(db, txn_a, &cursor_a, 0); assert_zero(r);
+ DBC *cursor_b = NULL;
+ r = db->cursor(db, txn_b, &cursor_b, DB_RMW); assert_zero(r);
+
+ r = prelock_range(cursor_a, htonl(10), htonl(100)); assert_zero(r);
+ r = prelock_range(cursor_b, htonl(50), htonl(200)); assert(r == expect_r);
+
+ r = cursor_a->c_close(cursor_a); assert_zero(r);
+ r = cursor_b->c_close(cursor_b); assert_zero(r);
+
+ r = txn_a->commit(txn_a, 0); assert_zero(r);
+ r = txn_b->commit(txn_b, 0); assert_zero(r);
+}
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+
+ const char *env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "prelocktest";
+
+ parse_args(argc, argv);
+
+ char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
+ r = system(rm_cmd); assert_zero(r);
+
+ r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ r = env->open(env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+ DB_TXN *create_txn = NULL;
+ r = env->txn_begin(env, NULL, &create_txn, 0); assert_zero(r);
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+ r = create_txn->commit(create_txn, 0); assert_zero(r);
+
+ test_read_write(env, db, DB_SERIALIZABLE, DB_LOCK_NOTGRANTED);
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/prelock-write-read.cc b/storage/tokudb/PerconaFT/src/tests/prelock-write-read.cc
new file mode 100644
index 00000000000..a03fe7e4150
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/prelock-write-read.cc
@@ -0,0 +1,106 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that prelocking a write range that overlapping a read lock conflicts
+
+static int prelock_range(DBC *cursor, int left, int right) {
+ DBT key_left; dbt_init(&key_left, &left, sizeof left);
+ DBT key_right; dbt_init(&key_right, &right, sizeof right);
+ int r = cursor->c_set_bounds(cursor, &key_left, &key_right, true, 0);
+ return r;
+}
+
+static void test_write_read(DB_ENV *env, DB *db, uint32_t iso_flags, int expect_r) {
+ int r;
+
+ DB_TXN *txn_a = NULL;
+ r = env->txn_begin(env, NULL, &txn_a, iso_flags); assert_zero(r);
+ DB_TXN *txn_b = NULL;
+ r = env->txn_begin(env, NULL, &txn_b, iso_flags); assert_zero(r);
+
+ DBC *cursor_a = NULL;
+ r = db->cursor(db, txn_a, &cursor_a, DB_RMW); assert_zero(r);
+ DBC *cursor_b = NULL;
+ r = db->cursor(db, txn_b, &cursor_b, 0); assert_zero(r);
+
+ r = prelock_range(cursor_a, htonl(10), htonl(100)); assert_zero(r);
+ r = prelock_range(cursor_b, htonl(50), htonl(200)); assert(r == expect_r);
+
+ r = cursor_a->c_close(cursor_a); assert_zero(r);
+ r = cursor_b->c_close(cursor_b); assert_zero(r);
+
+ r = txn_a->commit(txn_a, 0); assert_zero(r);
+ r = txn_b->commit(txn_b, 0); assert_zero(r);
+}
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+
+ const char *env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "prelocktest";
+
+ parse_args(argc, argv);
+
+ char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
+ r = system(rm_cmd); assert_zero(r);
+
+ r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ r = env->open(env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+ DB_TXN *create_txn = NULL;
+ r = env->txn_begin(env, NULL, &create_txn, 0); assert_zero(r);
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+ r = create_txn->commit(create_txn, 0); assert_zero(r);
+
+ test_write_read(env, db, DB_SERIALIZABLE, DB_LOCK_NOTGRANTED);
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/prelock-write-write.cc b/storage/tokudb/PerconaFT/src/tests/prelock-write-write.cc
new file mode 100644
index 00000000000..5f1383e675f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/prelock-write-write.cc
@@ -0,0 +1,106 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that prelocking a write range that overlaps a write lock conflicts
+
+static int prelock_range(DBC *cursor, int left, int right) {
+ DBT key_left; dbt_init(&key_left, &left, sizeof left);
+ DBT key_right; dbt_init(&key_right, &right, sizeof right);
+ int r = cursor->c_set_bounds(cursor, &key_left, &key_right, true, 0);
+ return r;
+}
+
+static void test_write_write(DB_ENV *env, DB *db, uint32_t iso_flags, int expect_r) {
+ int r;
+
+ DB_TXN *txn_a = NULL;
+ r = env->txn_begin(env, NULL, &txn_a, iso_flags); assert_zero(r);
+ DB_TXN *txn_b = NULL;
+ r = env->txn_begin(env, NULL, &txn_b, iso_flags); assert_zero(r);
+
+ DBC *cursor_a = NULL;
+ r = db->cursor(db, txn_a, &cursor_a, DB_RMW); assert_zero(r);
+ DBC *cursor_b = NULL;
+ r = db->cursor(db, txn_b, &cursor_b, DB_RMW); assert_zero(r);
+
+ r = prelock_range(cursor_a, htonl(10), htonl(100)); assert_zero(r);
+ r = prelock_range(cursor_b, htonl(50), htonl(200)); assert(r == expect_r);
+
+ r = cursor_a->c_close(cursor_a); assert_zero(r);
+ r = cursor_b->c_close(cursor_b); assert_zero(r);
+
+ r = txn_a->commit(txn_a, 0); assert_zero(r);
+ r = txn_b->commit(txn_b, 0); assert_zero(r);
+}
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+
+ const char *env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "prelocktest";
+
+ parse_args(argc, argv);
+
+ char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
+ r = system(rm_cmd); assert_zero(r);
+
+ r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ r = env->open(env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+ DB_TXN *create_txn = NULL;
+ r = env->txn_begin(env, NULL, &create_txn, 0); assert_zero(r);
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+ r = create_txn->commit(create_txn, 0); assert_zero(r);
+
+ test_write_write(env, db, DB_SERIALIZABLE, DB_LOCK_NOTGRANTED);
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/print_engine_status.cc b/storage/tokudb/PerconaFT/src/tests/print_engine_status.cc
new file mode 100644
index 00000000000..decad031052
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/print_engine_status.cc
@@ -0,0 +1,177 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Purpose of this test is to verify the basic functioning
+ * of the engine status functions.
+ */
+
+
+#include "test.h"
+#include <db.h>
+#include "toku_time.h"
+
+static DB_ENV *env;
+
+#define FLAGS_NOLOG DB_INIT_LOCK|DB_INIT_MPOOL|DB_CREATE|DB_PRIVATE
+#define FLAGS_LOG FLAGS_NOLOG|DB_INIT_TXN|DB_INIT_LOG
+
+static int mode = S_IRWXU+S_IRWXG+S_IRWXO;
+
+static void test_shutdown(void);
+
+static void
+test_shutdown(void) {
+ int r;
+ r=env->close(env, 0); CKERR(r);
+ env = NULL;
+}
+
+static void
+setup (uint32_t flags) {
+ int r;
+ if (env)
+ test_shutdown();
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r=db_env_create(&env, 0);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, flags, mode);
+ CKERR(r);
+}
+
+
+static void
+print_raw(TOKU_ENGINE_STATUS_ROW row) {
+ printf("keyname is %s, type is %d, legend is %s\n",
+ row->keyname,
+ row->type,
+ row->legend);
+}
+
+static void
+status_format_time(const time_t *timer, char *buf) {
+ ctime_r(timer, buf);
+ size_t len = strlen(buf);
+ assert(len < 26);
+ char end;
+
+ assert(len>=1);
+ end = buf[len-1];
+ while (end == '\n' || end == '\r') {
+ buf[len-1] = '\0';
+ len--;
+ assert(len>=1);
+ end = buf[len-1];
+ }
+}
+
+
+int
+test_main (int argc, char * const argv[]) {
+ uint64_t nrows;
+ uint64_t max_rows;
+ fs_redzone_state redzone_state;
+ uint64_t panic;
+ const int panic_string_len = 1024;
+ char panic_string[panic_string_len];
+
+ // char buf[bufsiz] = {'\0'};
+ parse_args(argc, argv);
+ setup(FLAGS_LOG);
+ env->txn_checkpoint(env, 0, 0, 0);
+
+ env->get_engine_status_num_rows(env, &max_rows);
+ TOKU_ENGINE_STATUS_ROW_S mystat[max_rows];
+ int r = env->get_engine_status (env, mystat, max_rows, &nrows, &redzone_state, &panic, panic_string, panic_string_len, TOKU_ENGINE_STATUS);
+ assert(r==0);
+
+ if (verbose) {
+ printf("First all the raw fields:\n");
+ for (uint64_t i = 0; i < nrows; i++) {
+ printf("%s ", mystat[i].keyname);
+ printf("%s ", mystat[i].columnname ? mystat[i].columnname : "(null)");
+ printf("%s ", mystat[i].legend);
+ printf("type=%d val = ", mystat[i].type);
+ switch(mystat[i].type) {
+ case FS_STATE:
+ printf("fs_state not supported yet, code is %" PRIu64 "\n", mystat[i].value.num);
+ break;
+ case UINT64:
+ printf("%" PRIu64 "\n", mystat[i].value.num);
+ break;
+ case CHARSTR:
+ printf("%s\n", mystat[i].value.str);
+ break;
+ case UNIXTIME:
+ {
+ char tbuf[26];
+ status_format_time((time_t*)&mystat[i].value.num, tbuf);
+ printf("%s\n", tbuf);
+ }
+ break;
+ case TOKUTIME:
+ {
+ double t = tokutime_to_seconds(mystat[i].value.num);
+ printf("%.6f\n", t);
+ }
+ break;
+ default:
+ printf("UNKNOWN STATUS TYPE:\n");
+ print_raw(&mystat[i]);
+ break;
+ }
+ }
+
+ printf("\n\n\n\n\nNow as reported by get_engine_status_text():\n\n");
+
+ int bufsiz = nrows * 128; // assume 128 characters per row
+ char buff[bufsiz];
+ r = env->get_engine_status_text(env, buff, bufsiz);
+ printf("%s", buff);
+
+ printf("\n\n\n\n\nFinally, print as reported by test utility print_engine_status()\n");
+
+ print_engine_status(env);
+
+ printf("That's all, folks.\n");
+ }
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/progress.cc b/storage/tokudb/PerconaFT/src/tests/progress.cc
new file mode 100644
index 00000000000..561da118146
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/progress.cc
@@ -0,0 +1,445 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/*
+ - ydb layer test of progress report on commit, abort.
+ - test1:
+ create two txns
+ perform operations (inserts and deletes)
+ commit or abort inner txn
+ if abort, verify progress callback was called with correct args
+ if commit, verify progress callback was not called
+ commit or abort outer txn
+ verify progress callback was called with correct args
+
+ Note: inner loop ends with commit, so when outer loop completes,
+ it should be called for all operations performed by inner loop.
+
+ perform_ops {
+ for i = 0 -> 5 {
+ for j = 0 -> 1023
+ if (j & 0x20) insert
+ else op_delete
+ }
+
+ verify (n) {
+ verify that callback was called n times with correct args
+ }
+
+ test1:
+ for c0 = 0, 1 {
+ for c1 = 0, 1 {
+ begin txn0
+ perform_ops (txn0)
+ begin txn1
+ perform ops (tnx1)
+ if c1
+ abort txn1
+ verify (n)
+ else
+ commit txn1
+ verify (0)
+ }
+ if c0
+ abort txn0
+ verify (2n)
+ else
+ commit txn0
+ verify (2n)
+ }
+
+
+ - test2
+ - create empty dictionary
+ - begin txn
+ - lock empty dictionary (full range lock)
+ - abort
+ - verify that callback was called twice, first with stalled-on-checkpoint true, then with stalled-on-checkpoint false
+
+
+*/
+
+
+#define DICT_0 "dict_0.db"
+static DB_ENV *env = NULL;
+static DB_TXN *txn_parent = NULL;
+static DB_TXN *txn_child = NULL;
+static DB_TXN *txn_hold_dname_lock = NULL;
+static DB *db;
+static const char *dname = DICT_0;
+static DBT key;
+static DBT val;
+
+
+static void start_txn(void);
+static void commit_txn(int);
+static void open_db(void);
+static void close_db(void);
+static void insert(void);
+static void op_delete(void);
+static void
+
+start_env(void) {
+ assert(env==NULL);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ dname = DICT_0;
+
+ dbt_init(&key, "key", strlen("key")+1);
+ dbt_init(&val, "val", strlen("val")+1);
+
+ open_db();
+ close_db();
+}
+
+static void
+end_env(void) {
+ int r;
+ r=env->close(env, 0);
+ CKERR(r);
+ env = NULL;
+}
+
+static void
+start_txn_prevent_dname_lock(void) {
+ assert(env!=NULL);
+ assert(txn_hold_dname_lock==NULL);
+ int r;
+ r=env->txn_begin(env, 0, &txn_hold_dname_lock, 0);
+ CKERR(r);
+ DB *db2;
+
+ r = db_create(&db2, env, 0);
+ CKERR(r);
+
+ r=db2->open(db2, txn_hold_dname_lock, dname, 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = db2->close(db2, 0);
+}
+
+static void nopoll(TOKU_TXN_PROGRESS UU(progress), void *UU(extra)) {
+ assert(false);
+}
+
+static void
+commit_txn_prevent_dname_lock(void) {
+ assert(env!=NULL);
+ assert(txn_hold_dname_lock!=NULL);
+ int r;
+ r = txn_hold_dname_lock->commit_with_progress(txn_hold_dname_lock, 0, nopoll, NULL);
+ CKERR(r);
+ txn_hold_dname_lock = NULL;
+}
+
+static void
+start_txn(void) {
+ assert(env!=NULL);
+ int r;
+ if (!txn_parent) {
+ r=env->txn_begin(env, 0, &txn_parent, 0);
+ }
+ else {
+ assert(!txn_child);
+ r=env->txn_begin(env, txn_parent, &txn_child, 0);
+ }
+ CKERR(r);
+}
+
+struct progress_expect {
+ int num_calls;
+ uint8_t is_commit_expected;
+ uint8_t stalled_on_checkpoint_expected;
+ uint64_t min_entries_total_expected;
+ uint64_t last_entries_processed;
+};
+
+static void poll(TOKU_TXN_PROGRESS progress, void *extra) {
+ struct progress_expect *CAST_FROM_VOIDP(info, extra);
+ info->num_calls++;
+ assert(progress->is_commit == info->is_commit_expected);
+ assert(progress->stalled_on_checkpoint == info->stalled_on_checkpoint_expected);
+ assert(progress->entries_total >= info->min_entries_total_expected);
+ assert(progress->entries_processed == 1024 + info->last_entries_processed);
+ info->last_entries_processed = progress->entries_processed;
+}
+
+//expect_number_polls is number of times polling function should be called.
+static void
+abort_txn(int expect_number_polls) {
+ assert(env!=NULL);
+ DB_TXN *txn;
+ bool child;
+ if (txn_child) {
+ txn = txn_child;
+ child = true;
+ }
+ else {
+ txn = txn_parent;
+ child = false;
+ }
+ assert(txn);
+
+ struct progress_expect extra = {
+ .num_calls = 0,
+ .is_commit_expected = 0,
+ .stalled_on_checkpoint_expected = 0,
+ .min_entries_total_expected = (uint64_t) expect_number_polls * 1024,
+ .last_entries_processed = 0
+ };
+
+ int r;
+ r=txn->abort_with_progress(txn, poll, &extra);
+ CKERR(r);
+ assert(extra.num_calls == expect_number_polls);
+ if (child)
+ txn_child = NULL;
+ else
+ txn_parent = NULL;
+}
+
+static void
+commit_txn(int expect_number_polls) {
+ assert(env!=NULL);
+ DB_TXN *txn;
+ bool child;
+ if (txn_child) {
+ txn = txn_child;
+ child = true;
+ }
+ else {
+ txn = txn_parent;
+ child = false;
+ }
+ assert(txn);
+ if (child)
+ assert(expect_number_polls == 0);
+
+ struct progress_expect extra = {
+ .num_calls = 0,
+ .is_commit_expected = 1,
+ .stalled_on_checkpoint_expected = 0,
+ .min_entries_total_expected = (uint64_t) expect_number_polls * 1024,
+ .last_entries_processed = 0
+ };
+
+ int r;
+ r=txn->commit_with_progress(txn, 0, poll, &extra);
+ CKERR(r);
+ assert(extra.num_calls == expect_number_polls);
+ if (child)
+ txn_child = NULL;
+ else
+ txn_parent = NULL;
+}
+
+static void
+open_db(void) {
+ assert(env!=NULL);
+ assert(db == NULL);
+
+ int r;
+
+ r = db_create(&db, env, 0);
+ CKERR(r);
+
+ r=db->open(db, NULL, dname, 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+}
+
+static void
+close_db(void) {
+ assert(env!=NULL);
+ assert(db != NULL);
+
+ int r;
+ r = db->close(db, 0);
+ CKERR(r);
+ db = NULL;
+}
+
+static void
+insert(void) {
+ assert(env!=NULL);
+ assert(db!=NULL);
+ DB_TXN *txn = txn_child ? txn_child : txn_parent;
+ assert(txn);
+
+ int r=db->put(db, txn,
+ &key,
+ &val,
+ 0);
+ CKERR(r);
+}
+
+static void
+op_delete(void) {
+ assert(env!=NULL);
+ assert(db!=NULL);
+ DB_TXN *txn = txn_child ? txn_child : txn_parent;
+ assert(txn);
+
+ int r=db->del(db, txn,
+ &key,
+ DB_DELETE_ANY);
+ CKERR(r);
+}
+
+static void
+perform_ops(int n) {
+ int i;
+ int j;
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < 1024; j++) {
+ if (j & 0x20)
+ op_delete();
+ else
+ insert();
+ }
+ }
+}
+
+static void
+progress_test_1(int n, int commit) {
+ start_env();
+ open_db();
+ {
+ start_txn();
+ {
+ start_txn();
+ perform_ops(n);
+ abort_txn(n);
+ }
+ {
+ start_txn();
+ perform_ops(n);
+ commit_txn(0);
+ }
+ perform_ops(n);
+ if (commit)
+ commit_txn(2*n);
+ else
+ abort_txn(2*n);
+ }
+ close_db();
+ end_env();
+}
+
+static void
+abort_txn_stall_checkpoint(void) {
+ //We have disabled the norollback log fallback optimization.
+ //Checkpoint will not stall
+ assert(env!=NULL);
+ assert(txn_parent);
+ assert(!txn_child);
+
+ int r;
+ r=txn_parent->abort_with_progress(txn_parent, nopoll, NULL);
+ CKERR(r);
+ txn_parent = NULL;
+}
+
+static void
+abort_txn_nostall_checkpoint(void) {
+ assert(env!=NULL);
+ assert(txn_parent);
+ assert(!txn_child);
+
+ int r;
+ r=txn_parent->abort_with_progress(txn_parent, nopoll, NULL);
+ CKERR(r);
+ txn_parent = NULL;
+}
+
+
+static void
+lock(void) {
+ assert(env!=NULL);
+ assert(db!=NULL);
+ assert(txn_parent);
+ assert(!txn_child);
+
+ int r=db->pre_acquire_table_lock(db, txn_parent);
+ CKERR(r);
+}
+
+static void
+progress_test_2(void) {
+ start_env();
+ open_db();
+ start_txn();
+ start_txn_prevent_dname_lock();
+ lock();
+ commit_txn_prevent_dname_lock();
+ abort_txn_stall_checkpoint();
+ close_db();
+ end_env();
+}
+
+static void
+progress_test_3(void) {
+ start_env();
+ open_db();
+ start_txn();
+ lock();
+ abort_txn_nostall_checkpoint();
+ close_db();
+ end_env();
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ parse_args(argc, argv);
+ int commit;
+ for (commit = 0; commit <= 1; commit++) {
+ progress_test_1(4, commit);
+ }
+ progress_test_2();
+ progress_test_3();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/put-del-multiple-array-indexing.cc b/storage/tokudb/PerconaFT/src/tests/put-del-multiple-array-indexing.cc
new file mode 100644
index 00000000000..69f533f0189
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/put-del-multiple-array-indexing.cc
@@ -0,0 +1,371 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that put_multiple inserts the correct rows into N dictionaries
+// verify that pu_multiple locks the correct keys for N dictionaries
+
+const int max_rows_per_primary = 9;
+
+static uint32_t
+get_total_secondary_rows(uint32_t num_primary) {
+ assert((num_primary % (max_rows_per_primary+1)) == 0);
+ return num_primary / (max_rows_per_primary+1) *
+ ( (max_rows_per_primary) * (max_rows_per_primary+1) / 2 );
+}
+
+static uint8_t
+get_num_keys(uint16_t i, uint8_t dbnum) {
+ return (i+dbnum) % (max_rows_per_primary + 1); // 0..9.. 10 choices
+}
+
+static uint16_t
+get_total_num_keys(uint16_t i, uint8_t num_dbs) {
+ uint16_t sum = 0;
+ for (uint8_t db = 0; db < num_dbs; ++db) {
+ sum += get_num_keys(i, db);
+ }
+ return sum;
+}
+
+static uint32_t
+get_key(uint16_t i, uint8_t dbnum, uint8_t which) {
+ uint32_t i32 = i;
+ uint32_t dbnum32 = dbnum;
+ uint32_t which32 = which;
+ uint32_t x = (dbnum32<<24) | (i32) | (which32<<8);
+ return x;
+}
+
+static void
+get_data(uint32_t *v, uint8_t i, uint8_t ndbs) {
+ int index = 0;
+ for (uint8_t dbnum = 0; dbnum < ndbs; dbnum++) {
+ for (uint8_t which = 0; which < get_num_keys(i, dbnum); ++which) {
+ v[index++] = get_key(i, dbnum, which);
+ }
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ (void) src_val;
+ uint8_t dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+
+ assert(dbnum > 0); // Does not get called for primary.
+ assert(dest_db != src_db);
+
+ assert(src_key->size == 2);
+ uint16_t i = *(uint16_t*)src_key->data;
+ uint8_t num_keys = get_num_keys(i, dbnum);
+
+ toku_dbt_array_resize(dest_keys, num_keys);
+ if (dest_vals) {
+ toku_dbt_array_resize(dest_vals, num_keys);
+ }
+
+ for (uint8_t which = 0; which < num_keys; ++which) {
+ DBT *dest_key = &dest_keys->dbts[which];
+
+ assert(dest_key->flags == DB_DBT_REALLOC);
+ {
+ // Memory management
+ if (dest_key->ulen < sizeof(uint32_t)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(uint32_t));
+ dest_key->ulen = sizeof(uint32_t);
+ }
+ dest_key->size = sizeof(uint32_t);
+ }
+ *(uint32_t*)dest_key->data = get_key(i, dbnum, which);
+
+ if (dest_vals) {
+ DBT *dest_val = &dest_vals->dbts[which];
+ dest_val->flags = 0;
+ dest_val->data = nullptr;
+ dest_val->size = 0;
+ }
+ }
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_keys, NULL, src_key, src_data);
+}
+
+static void
+verify_locked(DB_ENV *env, DB *db, uint8_t dbnum, uint16_t i) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ if (dbnum == 0) {
+ DBT key; dbt_init(&key, &i, sizeof i);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); CKERR2(r, DB_LOCK_NOTGRANTED);
+ } else {
+ for (uint8_t which = 0; which < get_num_keys(i, dbnum); ++which) {
+ uint32_t k = get_key(i, dbnum, which);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); CKERR2(r, DB_LOCK_NOTGRANTED);
+ }
+ }
+ r = txn->abort(txn); assert_zero(r);
+}
+
+static void
+verify_seq_primary(DB_ENV *env, DB *db, int dbnum, int ndbs, int nrows) {
+ assert(dbnum==0);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ uint16_t k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == i);
+
+ uint32_t total_rows = get_total_num_keys(i, ndbs);
+ assert(val.size == total_rows * sizeof (uint32_t));
+ uint32_t v[total_rows]; get_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ }
+ assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_seq(DB_ENV *env, DB *db, uint8_t dbnum, uint8_t ndbs, uint16_t nrows_primary) {
+ assert(dbnum > 0);
+ assert(dbnum < ndbs);
+ uint32_t nrows = get_total_secondary_rows(nrows_primary);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ uint16_t rows_found = 0;
+ uint16_t source_i = 0;
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ for (source_i = 0; source_i < nrows_primary; ++source_i) {
+ uint8_t num_keys = get_num_keys(source_i, dbnum);
+ for (uint8_t which = 0; which < num_keys; ++which) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ CKERR(r);
+ uint32_t k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == get_key(source_i, dbnum, which));
+ assert(val.size == 0);
+ rows_found++;
+ }
+ }
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ CKERR2(r, DB_NOTFOUND);
+ assert(rows_found == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ verify_seq_primary(env, db[0], 0, ndbs, nrows);
+ for (int dbnum = 1; dbnum < ndbs; dbnum++)
+ verify_seq(env, db[dbnum], dbnum, ndbs, nrows);
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_del(DB_ENV *env, DB *db[], int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_empty(env, db[dbnum]);
+}
+
+static void
+populate(DB_ENV *env, DB *db[], uint8_t ndbs, uint16_t nrows, bool del) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBT_ARRAY key_arrays[ndbs];
+ DBT_ARRAY val_arrays[ndbs];
+ for (uint8_t i = 0; i < ndbs; ++i) {
+ toku_dbt_array_init(&key_arrays[i], 1);
+ toku_dbt_array_init(&val_arrays[i], 1);
+ }
+ // populate
+ for (uint16_t i = 0; i < nrows; i++) {
+ uint32_t total_rows = get_total_num_keys(i, ndbs);
+ uint16_t k = i;
+ uint32_t v[total_rows]; get_data(v, i, ndbs);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ DBT pri_val; dbt_init(&pri_val, &v[0], sizeof v);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ if (del) {
+ r = env->del_multiple(env, db[0], txn, &pri_key, &pri_val, ndbs, db, key_arrays, flags);
+ } else {
+ r = env->put_multiple(env, db[0], txn, &pri_key, &pri_val, ndbs, db, key_arrays, val_arrays, flags);
+ }
+ assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_locked(env, db[dbnum], dbnum, i);
+ }
+ for (uint8_t i = 0; i < ndbs; ++i) {
+ toku_dbt_array_destroy(&key_arrays[i]);
+ toku_dbt_array_destroy(&val_arrays[i]);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (uint8_t dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert_zero(r);
+
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ populate(env, db, ndbs, nrows, false);
+
+ verify(env, db, ndbs, nrows);
+
+ populate(env, db, ndbs, nrows, true);
+
+ verify_del(env, db, ndbs);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 16;
+ int nrows = 100;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+ //rows should be divisible by max_rows + 1 (so that we have an equal number of each type and we know the total)
+ if (nrows % (max_rows_per_primary+1) != 0) {
+ nrows += (max_rows_per_primary+1) - (nrows % (max_rows_per_primary+1));
+ }
+ assert(ndbs >= 0);
+ assert(ndbs < (1<<8) - 1);
+ assert(nrows >= 0);
+ assert(nrows < (1<<15)); // Leave plenty of room
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/queries_with_deletes.cc b/storage/tokudb/PerconaFT/src/tests/queries_with_deletes.cc
new file mode 100644
index 00000000000..7e9f8af9133
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/queries_with_deletes.cc
@@ -0,0 +1,196 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// This test verifies that queries that have a provisional delete at the end of a basement node work.
+// The issue is that when we read off the end of a basement node, the next basement node may not be available memory, so we
+// need to release the ydb lock and try again. This test verifies that this scenario works by having many deletes
+// and a small cachetable.
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ // set a cachetable size of 10K
+ uint32_t cachesize = 100*1024;
+ // as part of #4503, arbitrarily increasing sizze of cachetable
+ // the idea is to make it small enough such that all data
+ // cannot fit in the cachetable, but big enough such that
+ // we don't have cachet pressure
+ r = env->set_cachesize(env, 0, 4*cachesize, 1); CKERR(r);
+ r = env->set_lg_bsize(env, 4096); CKERR(r);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ {
+ DB_TXN *txna;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 4096);
+ CKERR(r);
+ r = db->set_readpagesize(db, 1024);
+ CKERR(r);
+ r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ r = txna->commit(txna, 0); CKERR(r);
+ }
+ if (verbose) printf("starting insertion of even elements\n");
+ //
+ // now insert a bunch of elements
+ //
+ DB_TXN* txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ for (uint32_t i = 0; i < cachesize; i++) {
+ DBT key,val;
+ uint64_t key_data = 2*i;
+ uint64_t val_data = 4*i;
+ r = db->put(
+ db,
+ txn,
+ dbt_init(&key, &key_data, sizeof(key_data)),
+ dbt_init(&val, &val_data, sizeof(val_data)),
+ 0
+ );
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // this transaction will read all even keys inserted above
+ DB_TXN* txn_first = NULL;
+ r = env->txn_begin(env, NULL, &txn_first, DB_TXN_SNAPSHOT);
+ CKERR(r);
+
+ if (verbose) printf("starting insertion of odd elements and deletion of even elements\n");
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ for (uint32_t i = 0; i < cachesize; i++) {
+ //
+ // insert odd values, and delete even values
+ //
+ DBT key,val;
+ uint64_t key_data = 2*i+1;
+ uint64_t val_data = 4*i+2;
+ dbt_init(&key, &key_data, sizeof(key_data));
+ dbt_init(&val, &val_data, sizeof(val_data));
+ r = db->put(
+ db,
+ txn,
+ &key,
+ &val,
+ 0
+ );
+ CKERR(r);
+
+ key_data = 2*i;
+ r = db->del(db, txn, &key, DB_DELETE_ANY);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // this transaction will read all odd keys inserted in the second round
+ DB_TXN* txn_second = NULL;
+ r = env->txn_begin(env, NULL, &txn_second, DB_TXN_SNAPSHOT);
+ CKERR(r);
+
+ DBC* cursor_first = NULL;
+ DBC* cursor_second = NULL;
+ r = db->cursor(db, txn_first, &cursor_first, 0);
+ CKERR(r);
+ r = db->cursor(db, txn_second, &cursor_second, 0);
+ CKERR(r);
+
+ DBT key, val;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+ if (verbose) printf("starting cursor first query\n");
+ // now let's do the cursor reads and verify that all the data is read properly
+ for (uint32_t i = 0; i < cachesize; i++) {
+ r = cursor_first->c_get(cursor_first, &key, &val, DB_NEXT);
+ CKERR(r);
+ assert(key.size == 8);
+ assert(val.size == 8);
+ assert(*(uint64_t *)key.data == 2*i);
+ assert(*(uint64_t *)val.data == 4*i);
+ }
+ r = cursor_first->c_get(cursor_first, &key, &val, DB_NEXT);
+ CKERR2(r, DB_NOTFOUND);
+
+ if (verbose) printf("starting cursor second query\n");
+ // now let's do the cursor reads and verify that all the data is read properly
+ for (uint32_t i = 0; i < cachesize; i++) {
+ r = cursor_second->c_get(cursor_second, &key, &val, DB_NEXT);
+ CKERR(r);
+ assert(key.size == 8);
+ assert(val.size == 8);
+ assert(*(uint64_t *)key.data == 2*i+1);
+ assert(*(uint64_t *)val.data == 4*i+2);
+ }
+ r = cursor_second->c_get(cursor_second, &key, &val, DB_NEXT);
+ CKERR2(r, DB_NOTFOUND);
+
+ if (verbose) printf("cleaning up\n");
+
+ r = cursor_first->c_close(cursor_first);
+ CKERR(r);
+ r = cursor_second->c_close(cursor_second);
+ CKERR(r);
+
+ r = txn_first->commit(txn_first,0);
+ CKERR(r);
+ r = txn_second->commit(txn_second,0);
+ CKERR(r);
+
+ r = db->close(db, 0);
+ CKERR(r);
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-2483.cc b/storage/tokudb/PerconaFT/src/tests/recover-2483.cc
new file mode 100644
index 00000000000..5e556fe6a6c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-2483.cc
@@ -0,0 +1,201 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the table lock log entry is handled
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+DB_ENV *env;
+DB_TXN *tid;
+DB *db;
+DBT key,data;
+int i;
+enum {N=10000};
+char *keys[N];
+char *vals[N];
+
+static void
+do_x1_shutdown (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ {
+ DB_TXN *oldest;
+ r=env->txn_begin(env, 0, &oldest, 0);
+ CKERR(r);
+ }
+
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ {
+ DB_LOADER *loader;
+ DB *dbs[1] = {db};
+ uint32_t db_flags[1] = {DB_NOOVERWRITE};
+ uint32_t dbt_flags[1] = {0};
+ uint32_t loader_flags = 0;
+
+ r = env->create_loader(env, tid, &loader, NULL, 1, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ r = loader->set_error_callback(loader, NULL, NULL);
+ CKERR(r);
+ r = loader->set_poll_function(loader, NULL, NULL);
+ CKERR(r);
+ // close the loader
+ r = loader->close(loader);
+ CKERR(r);
+ }
+ for (i=0; i<N; i++) {
+ r=db->put(db, tid, dbt_init(&key, keys[i], strlen(keys[i])+1), dbt_init(&data, vals[i], strlen(vals[i])+1), 0); assert(r==0);
+ if (i%500==499) {
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ }
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+
+ //leave db open (prevent local checkpoint)
+
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool UU(did_commit)) {
+ int r;
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ for (i=0; i<N; i++) {
+ r=db->get(db, tid, dbt_init(&key, keys[i], 1+strlen(keys[i])), dbt_init_malloc(&data), 0); assert(r==0);
+ assert(strcmp((char*)data.data, vals[i])==0);
+ toku_free(data.data);
+ data.data=0;
+ if (i%500==499) {
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ }
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ toku_free(data.data);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+bool do_commit=false, do_recover_committed=false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ srandom(0xDEADBEEF);
+ for (i=0; i<N; i++) {
+ char ks[100]; snprintf(ks, sizeof(ks), "k%09ld.%d", random(), i);
+ char vs[1000]; snprintf(vs, sizeof(vs), "v%d.%0*d", i, (int)(sizeof(vs)-100), i);
+ keys[i]=toku_strdup(ks);
+ vals[i]=toku_strdup(vs);
+ }
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown();
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ }
+ for (i=0; i<N; i++) {
+ toku_free(keys[i]);
+ toku_free(vals[i]);
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-3113.cc b/storage/tokudb/PerconaFT/src/tests/recover-3113.cc
new file mode 100644
index 00000000000..82b11b6ca91
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-3113.cc
@@ -0,0 +1,178 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+
+/*****************
+ * Purpose: Verify fix for 3113
+ * Bug: Rollback log is checkpointed along with other cachefiles,
+ * but system crashes before checkpoint_end is written to recovery log.
+ * When recovery runs, it uses latest rollback log, which is out of synch
+ * with recovery log. Latest version of rollback log would be correct for
+ * last checkpoint if it completed, but version of rollback log needed
+ * is for last complete checkpoint.
+ * Fix: When opening rollback log for recovery, do not use latest, but use
+ * latest that is no newer than last complete checkpoint.
+ * Test: begin txn
+ * insert
+ * commit
+ * complete checkpoint (no live txns in checkpoint)
+ * begin txn
+ * insert
+ * begin checkpoint (txn in checkpointed rollback log)
+ * crash using callback2 (just before checkpoint_end is written to disk)
+ * attempt to recover, should crash with 3113
+ */
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const char *namea="a.db";
+static void checkpoint_callback_2(void * UU(extra));
+static DB_ENV *env;
+static bool do_test=false, do_recover=false;
+
+static void
+run_test(void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ // txn_begin; insert <a,a>; txn_abort
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "a", 2);
+ r = db->put(db, txn, &k, &v, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+
+ // checkpoint, no live txns in rollback log
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "b", 2);
+ dbt_init(&v, "b", 2);
+ r = db->put(db, txn, &k, &v, 0); CKERR(r);
+ }
+
+ // cause crash at next checkpoint, after xstillopen written, before checkpoint_end is written
+ db_env_set_checkpoint_callback2(checkpoint_callback_2, NULL);
+
+ // checkpoint, putting xstillopen in recovery log (txn is still active)
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+}
+
+
+static void checkpoint_callback_2(void * UU(extra)) {
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover (void) {
+ int r;
+
+ // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
+ // so recovery always runs over the entire log.
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+
+}
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char * cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+
+
+int
+test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+
+ if (do_test)
+ run_test();
+ else if (do_recover)
+ run_recover();
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-5146.cc b/storage/tokudb/PerconaFT/src/tests/recover-5146.cc
new file mode 100644
index 00000000000..491379760fe
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-5146.cc
@@ -0,0 +1,180 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+
+/*****************
+ * Purpose: Verify fix for 3113
+ * Bug: Rollback log is checkpointed along with other cachefiles,
+ * but system crashes before checkpoint_end is written to recovery log.
+ * When recovery runs, it uses latest rollback log, which is out of synch
+ * with recovery log. Latest version of rollback log would be correct for
+ * last checkpoint if it completed, but version of rollback log needed
+ * is for last complete checkpoint.
+ * Fix: When opening rollback log for recovery, do not use latest, but use
+ * latest that is no newer than last complete checkpoint.
+ * Test: begin txn
+ * insert
+ * commit
+ * complete checkpoint (no live txns in checkpoint)
+ * begin txn
+ * insert
+ * begin checkpoint (txn in checkpointed rollback log)
+ * crash using callback2 (just before checkpoint_end is written to disk)
+ * attempt to recover, should crash with 3113
+ */
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const char *namea="a.db";
+static DB_ENV *env;
+static bool do_test=false, do_recover=false;
+
+static void checkpoint_callback_2(void * UU(extra)) {
+ toku_hard_crash_on_purpose();
+}
+
+
+static void
+run_test(void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "a", 2);
+ r = db->put(db, txn, &k, &v, 0); CKERR(r);
+ uint8_t gid[DB_GID_SIZE];
+ memset(gid, 0, DB_GID_SIZE);
+ gid[0]=42;
+ r = txn->prepare(txn, gid, 0); CKERR(r);
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ DB_TXN *txn2;
+ {
+ r = env->txn_begin(env, NULL, &txn2, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "b", 2);
+ dbt_init(&v, "b", 2);
+ r = db->put(db, txn2, &k, &v, 0); CKERR(r);
+ }
+
+ // cause crash at next checkpoint, after xstillopen written, before checkpoint_end is written
+ db_env_set_checkpoint_callback2(checkpoint_callback_2, NULL);
+
+ // checkpoint, putting xstillopen in recovery log (txn is still active)
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+}
+
+static void run_recover (void) {
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ // recover the prepared transaction and commit it
+ DB_PREPLIST l[1];
+ long count=-1;
+ CKERR(env->txn_recover(env, l, 1, &count, DB_FIRST));
+ printf("%s:%d count=%ld\n", __FILE__, __LINE__, count);
+ assert(count==1);
+ assert(l[0].gid[0]==42);
+ r = l->txn->commit(l->txn, 0);
+ CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+
+}
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char * cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+
+
+int
+test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+
+ if (do_test)
+ run_test();
+ else if (do_recover)
+ run_recover();
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fcreate-fdelete-fcreate.cc b/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fcreate-fdelete-fcreate.cc
new file mode 100644
index 00000000000..1cd2e1dbd48
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fcreate-fdelete-fcreate.cc
@@ -0,0 +1,165 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// fcreate, fdelete, fcreate after a checkpoint
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *db;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ // create
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ // delete
+ r = env->dbremove(env, NULL, namea, NULL, 0); CKERR(r);
+
+ // create
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ uint32_t dbflags;
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = db->get_flags(db, &dbflags); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char *const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover_only();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fopen-abort.cc b/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fopen-abort.cc
new file mode 100644
index 00000000000..b38330054ab
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fopen-abort.cc
@@ -0,0 +1,249 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test verifies that db creation after a checkpoint works for nodup and dupsort dictionaries
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba, *dbb;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ uint32_t dbflags;
+ dbflags = 0;
+ r = dba->get_flags(dba, &dbflags); CKERR(r);
+ assert(dbflags == 0);
+
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ dbflags = 0;
+ r = dbb->get_flags(dbb, &dbflags); CKERR(r);
+ assert(dbflags == 0);
+
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DBT ba, bb;
+ dbt_init(&ba, NULL, 0);
+ dbt_init(&bb, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca,*cb;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ r = dbb->cursor(dbb, txn, &cb, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ int rb = cb->c_get(cb, &ba, &bb, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ assert(rb==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ assert(ba.size==2);
+ assert(bb.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(bb.data, &a, 2)==0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ assert(rb==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = cb->c_close(cb); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void run_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+}
+
+const char *cmd;
+
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0 || strcmp(argv[0], "--test") == 0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_commit) {
+ run_test(true, false);
+ } else if (do_abort) {
+ run_test(false, true);
+ } else if (do_recover_committed) {
+ run_recover(true);
+ } else if (do_recover_aborted) {
+ run_recover(false);
+ } else if (do_recover_only) {
+ run_recover_only();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fopen-commit.cc b/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fopen-commit.cc
new file mode 100644
index 00000000000..95da1d82e86
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-checkpoint-fopen-commit.cc
@@ -0,0 +1,249 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test verifies that db creation after a checkpoint works for nodup and dupsort dictionaries
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba, *dbb;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ uint32_t dbflags;
+ dbflags = 0;
+ r = dba->get_flags(dba, &dbflags); CKERR(r);
+ assert(dbflags == 0);
+
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ dbflags = 0;
+ r = dbb->get_flags(dbb, &dbflags); CKERR(r);
+ assert(dbflags == 0);
+
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DBT ba, bb;
+ dbt_init(&ba, NULL, 0);
+ dbt_init(&bb, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca,*cb;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ r = dbb->cursor(dbb, txn, &cb, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ int rb = cb->c_get(cb, &ba, &bb, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ assert(rb==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ assert(ba.size==2);
+ assert(bb.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(bb.data, &a, 2)==0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ assert(rb==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = cb->c_close(cb); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void run_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+}
+
+const char *cmd;
+
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_commit) {
+ run_test(true, false);
+ } else if (do_abort) {
+ run_test(false, true);
+ } else if (do_recover_committed) {
+ run_recover(true);
+ } else if (do_recover_aborted) {
+ run_recover(false);
+ } else if (do_recover_only) {
+ run_recover_only();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-child-rollback.cc b/storage/tokudb/PerconaFT/src/tests/recover-child-rollback.cc
new file mode 100644
index 00000000000..ea4a8294045
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-child-rollback.cc
@@ -0,0 +1,117 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - Grow the dictionary with insertions
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success. It also tests that snapshots
+// work correctly by verifying that table scans sum their vals to 0.
+//
+// This does NOT test:
+// - splits and merges
+// - multiple DBs
+//
+// Variables that are interesting to tweak and run:
+// - small cachetable
+// - number of elements
+//
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - one (or more) thread(s) constantly updating random values, wrapped in a persistent parent transaction.
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_update_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 0; i < cli_args->num_update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ myargs[i].do_prepare = true;
+ myargs[i].wrap_in_parent = true;
+ }
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, true, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ args.num_seconds = 5;
+ //args.txn_size = 64; // 100 * 256 is more than enough to spill (4096) byte rollback nodes for parent and child.
+ //args.val_size = 512; // Large values to overflow a rollback log node fast.
+ //args.env_args.node_size = 4*1024*1024; // Large nodes to prevent spending much time
+ //args.env_args.basement_node_size = 128*1024; // Large nodes to prevent spending much time
+ args.env_args.checkpointing_period = 1;
+ parse_stress_test_args(argc, argv, &args);
+ if (args.do_test_and_crash) {
+ stress_test_main(&args);
+ }
+ if (args.do_recover) {
+ stress_recover(&args);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-compare-db-descriptor.cc b/storage/tokudb/PerconaFT/src/tests/recover-compare-db-descriptor.cc
new file mode 100644
index 00000000000..249d26d2e3c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-compare-db-descriptor.cc
@@ -0,0 +1,330 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the comparison function get a valid db object pointer
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+char descriptor_contents[] = "Spoon full of sugar";
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static int my_compare(DB *UU(db), const DBT *a, const DBT *b) {
+ assert(db);
+ assert(db->cmp_descriptor);
+ assert(db->cmp_descriptor->dbt.size == sizeof(descriptor_contents));
+ assert(memcmp(db->cmp_descriptor->dbt.data, descriptor_contents, sizeof(descriptor_contents)) == 0);
+
+ assert(a->size == b->size);
+ return memcmp(a->data, b->data, a->size);
+}
+
+static void
+change_descriptor(DB_ENV* env, DB* db) {
+ DBT descriptor;
+ dbt_init(&descriptor, descriptor_contents, sizeof(descriptor_contents));
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db->change_descriptor(db, txn_desc, &descriptor, DB_UPDATE_CMP_DESCRIPTOR); CKERR(chk_r); }
+ });
+}
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char datadir[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(datadir, 2, TOKU_TEST_FILENAME, "data"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_data_dir(env, "data"); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ change_descriptor(env, dba);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ change_descriptor(env, dbb);
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = dba->put(dba, txn, &b, &a, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba, *dbb;
+ int r;
+ char datadir[TOKU_PATH_MAX+1];
+ toku_path_join(datadir, 2, TOKU_TEST_FILENAME, "data");
+ toku_os_recursive_delete(datadir);
+ r = toku_os_mkdir(datadir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_data_dir(env, "data"); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DBT ba, bb;
+ dbt_init(&ba, NULL, 0);
+ dbt_init(&bb, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca,*cb;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ r = dbb->cursor(dbb, txn, &cb, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ int rb = cb->c_get(cb, &ba, &bb, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ assert(rb==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ assert(ba.size==2);
+ assert(bb.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(bb.data, &a, 2)==0);
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == 0);
+ assert(aa.size == 2 && ab.size == 2 && memcmp(aa.data, b, 2) == 0 && memcmp(ab.data, a, 2) == 0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ assert(rb==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = cb->c_close(cb); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit)
+{
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ } else if (do_no_recover) {
+ do_x1_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-compare-db.cc b/storage/tokudb/PerconaFT/src/tests/recover-compare-db.cc
new file mode 100644
index 00000000000..02f2cbdca02
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-compare-db.cc
@@ -0,0 +1,306 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the comparison function get a valid db object pointer
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static int my_compare(DB *UU(db), const DBT *a, const DBT *b) {
+ assert(db);
+ assert(a->size == b->size);
+ return memcmp(a->data, b->data, a->size);
+}
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ r = dba->put(dba, txn, &b, &a, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba, *dbb;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DBT ba, bb;
+ dbt_init(&ba, NULL, 0);
+ dbt_init(&bb, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca,*cb;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ r = dbb->cursor(dbb, txn, &cb, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ int rb = cb->c_get(cb, &ba, &bb, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ assert(rb==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ assert(ba.size==2);
+ assert(bb.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(bb.data, &a, 2)==0);
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == 0);
+ assert(aa.size == 2 && ab.size == 2 && memcmp(aa.data, b, 2) == 0 && memcmp(ab.data, a, 2) == 0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ assert(rb==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = cb->c_close(cb); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit)
+{
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ } else if (do_no_recover) {
+ do_x1_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-del-multiple-abort.cc b/storage/tokudb/PerconaFT/src/tests/recover-del-multiple-abort.cc
new file mode 100644
index 00000000000..a8455c0f406
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-del-multiple-abort.cc
@@ -0,0 +1,285 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of a delete multiple log entry
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+ assert(src_db == NULL);
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else {
+ dest_val->size = 0;
+ }
+ break;
+ case DB_DBT_REALLOC:
+ assert(0);
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_keys, NULL, src_key, src_data);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0);
+ assert_zero(r);
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ DBT pri_val; dbt_init(&pri_val, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbs]; memset(vals, 0, sizeof vals);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_put_multiple_test_no_array(env, NULL, txn, &pri_key, &pri_val, ndbs, db, keys, vals, flags);
+ assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_data; dbt_init(&pri_data, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_del_multiple_test_no_array(env, NULL, txn, &pri_key, &pri_data, ndbs, db, keys, flags);
+ assert_zero(r);
+ }
+
+ toku_hard_crash_on_purpose();
+}
+
+static void
+verify_seq(DB_ENV *env, DB *db, int dbnum, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == get_key(i, dbnum));
+
+ if (dbnum == 0) {
+ assert(val.size == ndbs * sizeof (int));
+ int v[ndbs]; get_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ } else
+ assert(val.size == 0);
+ }
+ assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_all(DB_ENV *env, int ndbs, int nrows) {
+ int r;
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ verify_seq(env, db, dbnum, ndbs, nrows);
+ r = db->close(db, 0);
+ assert_zero(r);
+ }
+}
+
+static void
+run_recover(int ndbs, int nrows) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ verify_all(env, ndbs, nrows);
+ r = env->close(env, 0); assert_zero(r);
+}
+
+static int
+usage(void) {
+ return 1;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ int ndbs = 2;
+ int nrows = 1;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test)
+ run_test(ndbs, nrows);
+ if (do_recover)
+ run_recover(ndbs, nrows);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-del-multiple-srcdb-fdelete-all.cc b/storage/tokudb/PerconaFT/src/tests/recover-del-multiple-srcdb-fdelete-all.cc
new file mode 100644
index 00000000000..e823a74627d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-del-multiple-srcdb-fdelete-all.cc
@@ -0,0 +1,285 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of a delete multiple log entry
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = NULL;
+ if (dest_vals) {
+ toku_dbt_array_resize(dest_vals, 1);
+ dest_val = &dest_vals->dbts[0];
+ }
+
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+ assert(src_db != NULL);
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else {
+ dest_val->size = 0;
+ }
+ break;
+ case DB_DBT_REALLOC:
+ assert(0);
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_keys, NULL, src_key, src_data);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0);
+ assert_zero(r);
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ DBT pri_val; dbt_init(&pri_val, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbs]; memset(vals, 0, sizeof vals);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_put_multiple_test_no_array(env, ndbs > 0 ? db[0] : NULL, txn, &pri_key, &pri_val, ndbs, db, keys, vals, flags);
+ assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_data; dbt_init(&pri_data, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_del_multiple_test_no_array(env, ndbs > 0 ? db[0] : NULL, txn, &pri_key, &pri_data, ndbs, db, keys, flags);
+ assert_zero(r);
+ }
+
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = env->dbremove(env, txn, dbname, NULL, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ assert(r == DB_NOTFOUND);
+
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_all(DB_ENV *env, int ndbs) {
+ int r;
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ verify_empty(env, db);
+ r = db->close(db, 0);
+ assert_zero(r);
+ }
+}
+
+static void
+run_recover(int ndbs, int UU(nrows)) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ verify_all(env, ndbs);
+ r = env->close(env, 0); assert_zero(r);
+}
+
+static int
+usage(void) {
+ return 1;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ int ndbs = 2;
+ int nrows = 1;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test)
+ run_test(ndbs, nrows);
+ if (do_recover)
+ run_recover(ndbs, nrows);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-del-multiple.cc b/storage/tokudb/PerconaFT/src/tests/recover-del-multiple.cc
new file mode 100644
index 00000000000..c2ee80c438f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-del-multiple.cc
@@ -0,0 +1,277 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of a delete multiple log entry
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = NULL;
+ if (dest_vals) {
+ toku_dbt_array_resize(dest_vals, 1);
+ dest_val = &dest_vals->dbts[0];
+ }
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+ assert(src_db == NULL);
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else {
+ dest_val->size = 0;
+ }
+ break;
+ case DB_DBT_REALLOC:
+ assert(0);
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_keys, NULL, src_key, src_data);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0);
+ assert_zero(r);
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ DBT pri_val; dbt_init(&pri_val, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbs]; memset(vals, 0, sizeof vals);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_put_multiple_test_no_array(env, NULL, txn, &pri_key, &pri_val, ndbs, db, keys, vals, flags);
+ assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_data; dbt_init(&pri_data, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_del_multiple_test_no_array(env, NULL, txn, &pri_key, &pri_data, ndbs, db, keys, flags);
+ assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ assert(r == DB_NOTFOUND);
+
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_all(DB_ENV *env, int ndbs) {
+ int r;
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ verify_empty(env, db);
+ r = db->close(db, 0);
+ assert_zero(r);
+ }
+}
+
+static void
+run_recover(int ndbs, int UU(nrows)) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ verify_all(env, ndbs);
+ r = env->close(env, 0); assert_zero(r);
+}
+
+static int
+usage(void) {
+ return 1;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ int ndbs = 2;
+ int nrows = 1;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test)
+ run_test(ndbs, nrows);
+ if (do_recover)
+ run_recover(ndbs, nrows);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-delboth-after-checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-delboth-after-checkpoint.cc
new file mode 100644
index 00000000000..7b6d7b7cab4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-delboth-after-checkpoint.cc
@@ -0,0 +1,247 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test delboth commit before checkpoint
+
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+
+static void
+run_test (bool do_commit, bool do_abort) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ // insert (i,i) pairs
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ for (int i=0; i<256; i++) {
+ unsigned char c = (unsigned char) i;
+ DBT k = {.data=&c, .size=sizeof c};
+ DBT v = {.data=&c, .size=sizeof c};
+ r = dba->put(dba, txn, &k, &v, 0); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ // delete (128,128)
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ unsigned char c = 128;
+ DBT k = {.data=&c, .size=sizeof c};
+ r = dba->del(dba, txn, &k, 0); CKERR(r);
+ }
+
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+run_recover (bool UU(did_commit)) {
+ DB_ENV *env;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify all but (128,128) exist
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *ca;
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ int i;
+ for (i=0; ; i++) {
+ if (i == 128)
+ continue;
+ DBT k,v;
+ dbt_init(&k, NULL, 0);
+ dbt_init(&v, NULL, 0);
+ r = ca->c_get(ca, &k, &v, DB_NEXT);
+ if (r != 0)
+ break;
+ assert(k.size == 1 && v.size == 1);
+ unsigned char kk, vv;
+ memcpy(&kk, k.data, k.size);
+ memcpy(&vv, v.data, v.size);
+ assert(kk == i);
+ assert(vv == i);
+ }
+ assert(i == 256);
+
+ r = ca->c_close(ca); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = dba->close(dba, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+run_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ run_test (true, false);
+ } else if (do_abort) {
+ run_test (false, false);
+ } else if (do_explicit_abort) {
+ run_test(false, true);
+ } else if (do_recover_committed) {
+ run_recover(true);
+ } else if (do_recover_aborted) {
+ run_recover(false);
+ } else if (do_recover_only) {
+ run_recover_only();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-delboth-checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-delboth-checkpoint.cc
new file mode 100644
index 00000000000..8dd070a05d3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-delboth-checkpoint.cc
@@ -0,0 +1,247 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test delboth commit before checkpoint
+
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+
+static void
+run_test (bool do_commit, bool do_abort) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ // insert (i,i) pairs
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ for (int i=0; i<256; i++) {
+ unsigned char c = (unsigned char) i;
+ DBT k = {.data=&c, .size=sizeof c};
+ DBT v = {.data=&c, .size=sizeof c};
+ r = dba->put(dba, txn, &k, &v, 0); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // delete (128,128)
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ unsigned char c = 128;
+ DBT k = {.data=&c, .size=sizeof c};
+ r = dba->del(dba, txn, &k, 0); CKERR(r);
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+run_recover (bool UU(did_commit)) {
+ DB_ENV *env;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify all but (128,128) exist
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *ca;
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ int i;
+ for (i=0; ; i++) {
+ if (i == 128)
+ continue;
+ DBT k,v;
+ dbt_init(&k, NULL, 0);
+ dbt_init(&v, NULL, 0);
+ r = ca->c_get(ca, &k, &v, DB_NEXT);
+ if (r != 0)
+ break;
+ assert(k.size == 1 && v.size == 1);
+ unsigned char kk, vv;
+ memcpy(&kk, k.data, k.size);
+ memcpy(&vv, v.data, v.size);
+ assert(kk == i);
+ assert(vv == i);
+ }
+ assert(i == 256);
+
+ r = ca->c_close(ca); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = dba->close(dba, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+run_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ run_test (true, false);
+ } else if (do_abort) {
+ run_test (false, false);
+ } else if (do_explicit_abort) {
+ run_test(false, true);
+ } else if (do_recover_committed) {
+ run_recover(true);
+ } else if (do_recover_aborted) {
+ run_recover(false);
+ } else if (do_recover_only) {
+ run_recover_only();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor.cc
new file mode 100644
index 00000000000..6f9241b39d3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor.cc
@@ -0,0 +1,185 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor10.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor10.cc
new file mode 100644
index 00000000000..23b4c79ca41
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor10.cc
@@ -0,0 +1,201 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+bool do_crash;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ if (do_crash) {
+ toku_hard_crash_on_purpose();
+ }
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+
+ do_crash = false;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback2(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_checkpoint(env,0,0,0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ do_crash = true;
+ env->txn_checkpoint(env,0,0,0);
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor11.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor11.cc
new file mode 100644
index 00000000000..0c35ece7727
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor11.cc
@@ -0,0 +1,191 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+DB_ENV *env;
+DB_TXN* txn;
+DB_TXN* txn2;
+DB_TXN* txn3;
+DB *db;
+DB *db2;
+DB *db3;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ DBT desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+}
+
+
+static void run_test(void)
+{
+ txn = NULL;
+ txn2 = NULL;
+ txn3 = NULL;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback2(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+ { int chk_r = env->txn_checkpoint(env,0,0,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover(void)
+{
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor12.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor12.cc
new file mode 100644
index 00000000000..361808203bf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor12.cc
@@ -0,0 +1,191 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+DB_ENV *env;
+DB_TXN* txn;
+DB_TXN* txn2;
+DB_TXN* txn3;
+DB *db;
+DB *db2;
+DB *db3;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ DBT desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+}
+
+
+static void run_test(void)
+{
+ txn = NULL;
+ txn2 = NULL;
+ txn3 = NULL;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+ { int chk_r = env->txn_checkpoint(env,0,0,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover(void)
+{
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor2.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor2.cc
new file mode 100644
index 00000000000..c5120728d1d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor2.cc
@@ -0,0 +1,188 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+ { int chk_r = db2->close(db2,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+ { int chk_r = db3->close(db3,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor3.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor3.cc
new file mode 100644
index 00000000000..2139a75316c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor3.cc
@@ -0,0 +1,188 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = db2->close(db2,0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+ { int chk_r = db3->close(db3,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor4.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor4.cc
new file mode 100644
index 00000000000..c5f2eaf2b62
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor4.cc
@@ -0,0 +1,187 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_checkpoint(env,0,0,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor5.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor5.cc
new file mode 100644
index 00000000000..d538e2712d5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor5.cc
@@ -0,0 +1,187 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_checkpoint(env,0,0,0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor6.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor6.cc
new file mode 100644
index 00000000000..39dfaf2975d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor6.cc
@@ -0,0 +1,187 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_checkpoint(env,0,0,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor7.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor7.cc
new file mode 100644
index 00000000000..f31c5693d39
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor7.cc
@@ -0,0 +1,199 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+bool do_crash;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ if (do_crash) {
+ toku_hard_crash_on_purpose();
+ }
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+
+ do_crash = false;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+
+ do_crash = true;
+ env->txn_checkpoint(env,0,0,0);
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor8.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor8.cc
new file mode 100644
index 00000000000..38058459a0b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor8.cc
@@ -0,0 +1,201 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+bool do_crash;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ if (do_crash) {
+ toku_hard_crash_on_purpose();
+ }
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+
+ do_crash = false;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_checkpoint(env,0,0,0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ do_crash = true;
+ env->txn_checkpoint(env,0,0,0);
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-descriptor9.cc b/storage/tokudb/PerconaFT/src/tests/recover-descriptor9.cc
new file mode 100644
index 00000000000..ffc8a02cfcf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-descriptor9.cc
@@ -0,0 +1,199 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+bool do_crash;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ if (do_crash) {
+ toku_hard_crash_on_purpose();
+ }
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+ DB_TXN* txn;
+ DB_TXN* txn2;
+ DB_TXN* txn3;
+ DBT desc;
+
+ do_crash = false;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.size = sizeof(four_byte_desc);
+ desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback2(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, txn_2, "foo2.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn_2, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ });
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, txn_3, "foo3.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn_3, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ });
+
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn2, 0); CKERR(chk_r); }
+ { int chk_r = db2->change_descriptor(db2, txn2, &desc, 0); CKERR(chk_r); }
+ { int chk_r = txn2->abort(txn2); CKERR(chk_r); }
+
+ { int chk_r = env->txn_begin(env, NULL, &txn3, 0); CKERR(chk_r); }
+ { int chk_r = db3->change_descriptor(db3, txn3, &desc, 0); CKERR(chk_r); }
+
+ do_crash = true;
+ env->txn_checkpoint(env,0,0,0);
+}
+
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+ DB *db2;
+ DB *db3;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo2.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db3, env, 0); CKERR(chk_r); }
+ { int chk_r = db3->open(db3, NULL, "foo3.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db3);
+ { int chk_r = db3->close(db3, 0); CKERR(chk_r); }
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fassociate.cc b/storage/tokudb/PerconaFT/src/tests/recover-fassociate.cc
new file mode 100644
index 00000000000..31093f13d05
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fassociate.cc
@@ -0,0 +1,165 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test makes sure that fassociate can open nodup and dupsort dictionaries
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ uint32_t dbflags;
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = dba->get_flags(dba, &dbflags); CKERR(r);
+ assert(dbflags == 0);
+ r = dba->close(dba, 0); CKERR(r);
+ DB *dbb;
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = dbb->get_flags(dbb, &dbflags); CKERR(r);
+ assert(dbflags == 0);
+ r = dbb->close(dbb, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fclose-in-checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-fclose-in-checkpoint.cc
new file mode 100644
index 00000000000..22b89a0f233
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fclose-in-checkpoint.cc
@@ -0,0 +1,157 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test fopen, checkpoint fclose
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+
+static void checkpoint_callback_closeit(void *extra) {
+ DB *db = (DB *) extra;
+ int r = db->close(db, 0); CKERR(r);
+}
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *txn;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // fcreate
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ // dummy transaction
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ // fopen
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ db_env_set_checkpoint_callback(checkpoint_callback_closeit, db);
+
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fcreate-basementnodesize.cc b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-basementnodesize.cc
new file mode 100644
index 00000000000..5206333842e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-basementnodesize.cc
@@ -0,0 +1,192 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify thtat we can create the correct tree type after the db is removed
+#include <sys/stat.h>
+#include "test.h"
+
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static const char *namea="a.db"; uint32_t nodesizea = 0;
+static const char *nameb="b.db"; uint32_t nodesizeb = 32*1024;
+
+static void do_remove(DB_ENV *env, const char *filename) {
+ int r;
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, filename, strlen(filename)+1);
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname); CKERR(r);
+ if (verbose) printf("%s -> %s\n", filename, (char *) iname.data);
+ char rmpath[TOKU_PATH_MAX+1];
+ toku_path_join(rmpath, 2, TOKU_TEST_FILENAME, iname.data);
+ toku_os_recursive_delete(rmpath);
+ toku_free(iname.data);
+}
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ // create a db with the default nodesize
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->get_readpagesize(dba, &nodesizea); CKERR(r);
+ if (verbose) printf("nodesizea=%u", nodesizea);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+
+ // create a db with a small nodesize
+ DB *dbb;
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->set_readpagesize(dbb, nodesizeb); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // remove the inames to force recovery to recreate them
+ do_remove(env, namea);
+ do_remove(env, nameb);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ int r;
+
+ // run recovery
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify that the trees have the correct nodesizes
+ uint32_t pagesize;
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = dba->get_readpagesize(dba, &pagesize); CKERR(r);
+ if (verbose) printf("%u\n", pagesize);
+ // assert(pagesize == nodesizea);
+ r = dba->close(dba, 0); CKERR(r);
+
+ DB *dbb;
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = dbb->get_readpagesize(dbb, &pagesize); CKERR(r);
+ if (verbose) printf("%u\n", pagesize);
+ assert(pagesize == nodesizeb);
+ r = dbb->close(dbb, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static const char *cmd;
+
+static bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fcreate-fclose.cc b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-fclose.cc
new file mode 100644
index 00000000000..1dc6f88bb3e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-fclose.cc
@@ -0,0 +1,147 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify thtat we can create the correct tree type after the db is removed
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *db;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+// r = db->close(db, 0); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fcreate-fdelete.cc b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-fdelete.cc
new file mode 100644
index 00000000000..09ebadd52ad
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-fdelete.cc
@@ -0,0 +1,156 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify thtat we can create the correct tree type after the db is removed
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *db;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->dbremove(env, NULL, namea, NULL, 0); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ uint32_t dbflags;
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = db->get_flags(db, &dbflags); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fcreate-nodesize.cc b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-nodesize.cc
new file mode 100644
index 00000000000..8b0ac7ab25a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-nodesize.cc
@@ -0,0 +1,193 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify thtat we can create the correct tree type after the db is removed
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static const char *namea="a.db"; uint32_t nodesizea = 0;
+static const char *nameb="b.db"; uint32_t nodesizeb = 64*1024;
+
+static void do_remove(DB_ENV *env, const char *filename) {
+ int r;
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, filename, strlen(filename)+1);
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname); CKERR(r);
+ if (verbose) printf("%s -> %s\n", filename, (char *) iname.data);
+ char rmpath[TOKU_PATH_MAX+1];
+ toku_path_join(rmpath, 2, TOKU_TEST_FILENAME, iname.data);
+ toku_os_recursive_delete(rmpath);
+ toku_free(iname.data);
+}
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ // create a db with the default nodesize
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->get_pagesize(dba, &nodesizea); CKERR(r);
+ if (verbose) printf("nodesizea=%u", nodesizea);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+
+ // create a db with a small nodesize
+ DB *dbb;
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->set_pagesize(dbb, nodesizeb); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // remove the inames to force recovery to recreate them
+ do_remove(env, namea);
+ do_remove(env, nameb);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ int r;
+
+ // run recovery
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify that the trees have the correct nodesizes
+ uint32_t pagesize;
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = dba->get_pagesize(dba, &pagesize); CKERR(r);
+ if (verbose) printf("%u\n", pagesize);
+ // assert(pagesize == nodesizea);
+ r = dba->close(dba, 0); CKERR(r);
+
+ DB *dbb;
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = dbb->get_pagesize(dbb, &pagesize); CKERR(r);
+ if (verbose) printf("%u\n", pagesize);
+ assert(pagesize == nodesizeb);
+ r = dbb->close(dbb, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static const char *cmd;
+
+static bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fcreate-xabort.cc b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-xabort.cc
new file mode 100644
index 00000000000..f42a180eef2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fcreate-xabort.cc
@@ -0,0 +1,143 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify thtat we can create the correct tree type after the db is removed
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *db;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, namea, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR2(r, ENOENT);
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt1.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt1.cc
new file mode 100644
index 00000000000..28baeb745be
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt1.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 1;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt10.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt10.cc
new file mode 100644
index 00000000000..ef0bdad0fdc
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt10.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 10;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt2.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt2.cc
new file mode 100644
index 00000000000..fc6a5fe2838
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt2.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 2;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt3.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt3.cc
new file mode 100644
index 00000000000..79dbe3db960
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt3.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 3;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt4.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt4.cc
new file mode 100644
index 00000000000..9139dd26cfd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt4.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 4;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt5.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt5.cc
new file mode 100644
index 00000000000..98cbe3a3a2d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt5.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 5;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt6.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt6.cc
new file mode 100644
index 00000000000..3aac0e8c668
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt6.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 6;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt7.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt7.cc
new file mode 100644
index 00000000000..e60df8ca0e1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt7.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 7;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt8.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt8.cc
new file mode 100644
index 00000000000..17b809ade0f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt8.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 8;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-flt9.cc b/storage/tokudb/PerconaFT/src/tests/recover-flt9.cc
new file mode 100644
index 00000000000..c620a413688
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-flt9.cc
@@ -0,0 +1,58 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+#include "recover-test_crash_in_flusher_thread.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ state_to_crash = 9;
+ return run_recover_flt_test(argc, argv);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fopen-checkpoint-fclose.cc b/storage/tokudb/PerconaFT/src/tests/recover-fopen-checkpoint-fclose.cc
new file mode 100644
index 00000000000..b08654494f1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fopen-checkpoint-fclose.cc
@@ -0,0 +1,153 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test fopen, checkpoint fclose
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *txn;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // fcreate
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ // dummy transaction
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ // fopen
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ // fclose
+ r = db->close(db, 0); CKERR(r);
+
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char *const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fopen-fclose-checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-fopen-fclose-checkpoint.cc
new file mode 100644
index 00000000000..595624fd864
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fopen-fclose-checkpoint.cc
@@ -0,0 +1,153 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test fopen, checkpoint fclose
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *txn;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // fcreate
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ // dummy transaction
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ // fopen
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ // fclose
+ r = db->close(db, 0); CKERR(r);
+
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-fopen-fdelete-checkpoint-fcreate.cc b/storage/tokudb/PerconaFT/src/tests/recover-fopen-fdelete-checkpoint-fcreate.cc
new file mode 100644
index 00000000000..4265adffa5f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-fopen-fdelete-checkpoint-fcreate.cc
@@ -0,0 +1,187 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that we can fcreate after fdelete with different treeflags
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void put_something(DB_ENV *env, DB *db, const char *k, const char *v) {
+ int r;
+ DBT key, val;
+ dbt_init(&key, k, strlen(k));
+ dbt_init(&val, v, strlen(v));
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->put(db, txn, &key, &val, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *txn;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // fcreate
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ // dummy transaction
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ // fopen
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ // insert something
+ put_something(env, db, "a", "b");
+
+ r = db->close(db, 0); CKERR(r);
+
+ // fdelete
+ r = env->dbremove(env, NULL, namea, NULL, 0); CKERR(r);
+
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // fcreate with different treeflags
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ // insert something
+ put_something(env, db, "c", "d");
+
+ r = db->close(db, 0); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ uint32_t dbflags;
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+ r = db->get_flags(db, &dbflags); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-hotindexer-simple-abort-put.cc b/storage/tokudb/PerconaFT/src/tests/recover-hotindexer-simple-abort-put.cc
new file mode 100644
index 00000000000..39aebc6653a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-hotindexer-simple-abort-put.cc
@@ -0,0 +1,146 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+
+ lazy_assert(src_db != NULL && dest_db != NULL);
+
+ if (dest_key->flags == DB_DBT_REALLOC) {
+ toku_free(dest_key->data);
+ }
+ dest_key->flags = DB_DBT_REALLOC;
+ dest_key->data = toku_xmemdup(src_val->data, src_val->size);
+ dest_key->size = src_val->size;
+ dest_val->size = 0;
+
+ return 0;
+}
+
+int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static void
+run_test(void) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ DB *src_db = NULL;
+ r = db_create(&src_db, env, 0); assert_zero(r);
+ r = src_db->open(src_db, NULL, "0.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *dest_db = NULL;
+ r = db_create(&dest_db, env, 0); assert_zero(r);
+ r = dest_db->open(dest_db, NULL, "1.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_TXN* index_txn = NULL;
+ r = env->txn_begin(env, NULL, &index_txn , 0); assert_zero(r);
+ DB_TXN* put_txn = NULL;
+ r = env->txn_begin(env, NULL, &put_txn , 0); assert_zero(r);
+
+ DBT key,data;
+ r = src_db->put(
+ src_db,
+ put_txn,
+ dbt_init(&key, "hello", 6),
+ dbt_init(&data, "there", 6),
+ 0
+ );
+
+ DB_INDEXER *indexer = NULL;
+ r = env->create_indexer(env, index_txn, &indexer, src_db, 1, &dest_db, NULL, 0); assert_zero(r);
+ r = indexer->build(indexer); assert_zero(r);
+ r = indexer->close(indexer); assert_zero(r);
+ r = index_txn->abort(index_txn); assert_zero(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ assert_zero(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void
+run_recover(void) {
+ DB_ENV *env;
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ }
+
+ if (do_test) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-loader-test.cc b/storage/tokudb/PerconaFT/src/tests/recover-loader-test.cc
new file mode 100644
index 00000000000..09ce645a5bc
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-loader-test.cc
@@ -0,0 +1,518 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* NOTE:
+ *
+ * Someday figure out a better way to verify inames that should not be
+ * in data dir after recovery. Currently, they are just hard-coded in
+ * the new_iname_str[] array. This will break when something changes,
+ * such as the xid of the transaction that creates the loader.
+ */
+
+
+/* Purpose is to verify that when a loader crashes:
+ * - there are no temp files remaining
+ * - the loader-generated iname file is not present
+ *
+ * In the event of a crash, the verification of no temp files and
+ * no loader-generated iname file is done after recovery.
+ *
+ * Mechanism:
+ * This test is derived from loader-cleanup-test, which was derived from loader-stress-test.
+ *
+ * The outline of the test is as follows:
+ * - use loader to create table
+ * - verify presence of temp files
+ * - crash
+ * - recover
+ * - verify absence of temp files
+ * - verify absence of unwanted iname files (new inames) - how?
+ *
+ *
+ */
+
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "ydb-internal.h"
+
+static const int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+
+#define NUM_DBS 5
+
+static bool do_test=false, do_recover=false;
+
+static DB_ENV *env;
+static int NUM_ROWS=50000000;
+static int COMPRESS=0;
+
+enum {MAX_NAME=128};
+enum {MAGIC=311};
+
+static DBT old_inames[NUM_DBS];
+static DBT new_inames[NUM_DBS];
+
+
+static char const * const new_iname_str[NUM_DBS] = {"qo_0000_35_c_L_0.tokudb",
+ "qo_0001_35_c_L_1.tokudb",
+ "qo_0002_35_c_L_2.tokudb",
+ "qo_0003_35_c_L_3.tokudb",
+ "qo_0004_35_c_L_4.tokudb"};
+
+static const char *loader_temp_prefix = "tokuld"; // 2536
+static int count_temp(char * dirname);
+static void get_inames(DBT* inames, DB** dbs);
+static int verify_file(char const * const dirname, char const * const filename);
+static int print_dir(char * dirname);
+
+// return number of temp files
+int
+count_temp(char * dirname) {
+ int n = 0;
+
+ DIR * dir = opendir(dirname);
+
+ struct dirent *ent;
+ while ((ent = readdir(dir)))
+ if ((ent->d_type == DT_REG || ent->d_type == DT_UNKNOWN) && strncmp(ent->d_name, loader_temp_prefix, 6) == 0)
+ n++;
+ closedir(dir);
+ return n;
+}
+
+// print contents of directory
+int
+print_dir(char * dirname) {
+ int n = 0;
+
+ DIR * dir = opendir(dirname);
+
+ struct dirent *ent;
+ while ((ent = readdir(dir))) {
+ if (ent->d_type == DT_REG || ent->d_type == DT_UNKNOWN) {
+ n++;
+ printf("File: %s\n", ent->d_name);
+ }
+ }
+ closedir(dir);
+ return n;
+}
+
+
+
+// return non-zero if file exists
+int
+verify_file(char const * const dirname, char const * const filename) {
+ int n = 0;
+ DIR * dir = opendir(dirname);
+
+ struct dirent *ent;
+ while ((ent=readdir(dir))) {
+ if ((ent->d_type==DT_REG || ent->d_type==DT_UNKNOWN) && strcmp(ent->d_name, filename)==0) {
+ n++;
+ }
+ }
+ closedir(dir);
+ return n;
+}
+
+void
+get_inames(DBT* inames, DB** dbs) {
+ int i;
+ for (i = 0; i < NUM_DBS; i++) {
+ DBT dname;
+ char * dname_str = dbs[i]->i->dname;
+ dbt_init(&dname, dname_str, strlen(dname_str)+1);
+ dbt_init(&(inames[i]), NULL, 0);
+ inames[i].flags |= DB_DBT_MALLOC;
+ int r = env->get_iname(env, &dname, &inames[i]);
+ CKERR(r);
+ char * iname_str = (char*) (inames[i].data);
+ // if (verbose)
+ printf("dname = %s, iname = %s\n", dname_str, iname_str);
+ }
+}
+
+
+#if 0
+void print_inames(DB** dbs);
+void
+print_inames(DB** dbs) {
+ int i;
+ for (i = 0; i < NUM_DBS; i++) {
+ DBT dname;
+ DBT iname;
+ char * dname_str = dbs[i]->i->dname;
+ dbt_init(&dname, dname_str, sizeof(dname_str));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ int r = env->get_iname(env, &dname, &iname);
+ CKERR(r);
+ char * iname_str = (char*)iname.data;
+ if (verbose) printf("dname = %s, iname = %s\n", dname_str, iname_str);
+ int n = verify_file(env->i->real_data_dir, iname_str);
+ assert(n == 1);
+ toku_free(iname.data);
+ }
+}
+#endif
+
+
+//
+// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
+//
+
+// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
+// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
+int a[NUM_DBS][32];
+int inv[NUM_DBS][32];
+
+
+// rotate right and left functions
+#if 0
+static inline unsigned int rotr32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x >> n) | ( x << (32 - n));
+}
+#endif
+static inline unsigned int rotl32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x << n) | ( x >> (32 - n));
+}
+
+static void generate_permute_tables(void) {
+ int i, j, tmp;
+ for(int db=0;db<NUM_DBS;db++) {
+ for(i=0;i<32;i++) {
+ a[db][i] = i;
+ }
+ for(i=0;i<32;i++) {
+ j = random() % (i + 1);
+ tmp = a[db][j];
+ a[db][j] = a[db][i];
+ a[db][i] = tmp;
+ }
+// if(db < NUM_DBS){ printf("a[%d] = ", db); for(i=0;i<32;i++) { printf("%2d ", a[db][i]); } printf("\n");}
+ for(i=0;i<32;i++) {
+ inv[db][a[db][i]] = i;
+ }
+ }
+}
+
+// permute bits of x based on permute table bitmap
+static unsigned int twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << a[db][i];
+ }
+ return b;
+}
+
+// generate val from key, index
+static unsigned int generate_val(int key, int i) {
+ return rotl32((key + MAGIC), i);
+}
+
+// There is no handlerton in this test, so this function is a local replacement
+// for the handlerton's generate_row_for_put().
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+
+ (void) src_db;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ if ( which == 0 ) {
+ if (dest_key->flags==DB_DBT_REALLOC) {
+ if (dest_key->data) toku_free(dest_key->data);
+ dest_key->flags = 0;
+ dest_key->ulen = 0;
+ }
+ if (dest_val->flags==DB_DBT_REALLOC) {
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_val->flags = 0;
+ dest_val->ulen = 0;
+ }
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+ }
+ else {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(unsigned int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(unsigned int));
+ dest_key->ulen = sizeof(unsigned int);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < sizeof(unsigned int)) {
+ dest_val->data = toku_xrealloc(dest_val->data, sizeof(unsigned int));
+ dest_val->ulen = sizeof(unsigned int);
+ }
+ unsigned int *new_key = (unsigned int *)dest_key->data;
+ unsigned int *new_val = (unsigned int *)dest_val->data;
+
+ *new_key = twiddle32(*(unsigned int*)src_key->data, which);
+ *new_val = generate_val(*(unsigned int*)src_key->data, which);
+
+ dest_key->size = sizeof(unsigned int);
+ dest_val->size = sizeof(unsigned int);
+ //data is already set above
+ }
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+
+static void *expect_poll_void = &expect_poll_void;
+
+static int poll_function (void *UU(extra), float UU(progress)) {
+ toku_hard_crash_on_purpose();
+ return -1;
+}
+
+static void test_loader(DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[NUM_DBS];
+ uint32_t dbt_flags[NUM_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = COMPRESS; // set with -p option
+
+ int n = count_temp(env->i->real_data_dir);
+ assert(n == 0); // Must be no temp files before loader is run
+
+ if (verbose) printf("old inames:\n");
+ get_inames(old_inames, dbs);
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+ r = loader->set_error_callback(loader, NULL, NULL);
+ CKERR(r);
+ r = loader->set_poll_function(loader, poll_function, expect_poll_void);
+ CKERR(r);
+
+ printf("COMPRESS = %d\n", COMPRESS);
+ if (verbose) printf("new inames:\n");
+ get_inames(new_inames, dbs);
+
+ // using loader->put, put values into DB
+ DBT key, val;
+ unsigned int k, v;
+ for(int i=1;i<=NUM_ROWS;i++) {
+ k = i;
+ v = generate_val(i, 0);
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ CKERR(r);
+ if (verbose) { if((i%10000) == 0){printf("."); fflush(stdout);} }
+ }
+ if( verbose) {printf("\n"); fflush(stdout);}
+
+ printf("Data dir is %s\n", env->i->real_data_dir);
+ n = count_temp(env->i->real_data_dir);
+ printf("Num temp files = %d\n", n);
+ assert(n); // test is useless unless at least one temp file is created
+ if (verbose) {
+ printf("Contents of data dir:\n");
+ print_dir(env->i->real_data_dir);
+ }
+ printf("closing, will crash\n"); fflush(stdout);
+ r = loader->close(loader);
+ printf("Should never return from loader->close()\n"); fflush(stdout);
+ assert(0);
+
+}
+
+
+static void run_test(void)
+{
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+// int envflags = DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ //Disable auto-checkpointing
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[NUM_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ generate_permute_tables();
+
+ test_loader(dbs);
+ printf("Should never return from test_loader\n"); fflush(stdout);
+ assert(0);
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+
+
+static void run_recover (void) {
+ int i;
+
+ // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
+ // so recovery always runs over the entire log.
+
+ // run recovery
+ int r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // now verify contents of data_dir, should be no temp files, no loader-created iname files
+ if (verbose)
+ print_dir(env->i->real_data_dir);
+
+ int n = count_temp(env->i->real_data_dir);
+ printf("Num temp files = %d\n", n);
+ assert(n==0); // There should be no temp files remaining after recovery
+
+ for (i = 0; i < NUM_DBS; i++) {
+ char const * const iname = new_iname_str[i];
+ r = verify_file(env->i->real_data_dir, iname);
+ if (r) {
+ printf("File %s exists, but it should not\n", iname);
+ }
+ assert(r == 0);
+ if (verbose)
+ printf("File has been properly deleted: %s\n", iname);
+ }
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+
+}
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+
+ if (do_test) {
+ printf("\n\n perform test, crash\n");
+ fflush(stdout);
+ run_test();
+ }
+ else if (do_recover) {
+ printf("\n\n perform recovery\n");
+ run_recover();
+ }
+ else {
+ printf("\n\n BOGUS!\n");
+ assert(0);
+ }
+
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -c -d <num_dbs> -r <num_rows>\n%s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-z")==0) {
+ COMPRESS = LOADER_COMPRESS_INTERMEDIATES;
+ printf("Compressing\n");
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-lsn-filter-multiple.cc b/storage/tokudb/PerconaFT/src/tests/recover-lsn-filter-multiple.cc
new file mode 100644
index 00000000000..7ea51595921
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-lsn-filter-multiple.cc
@@ -0,0 +1,248 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test makes sure the LSN filtering is used during recovery of put_multiple
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+enum {num_dbs = 2};
+static DBT dest_keys[num_dbs];
+static DBT dest_vals[num_dbs];
+
+bool do_test=false, do_recover=false;
+
+static int
+put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ if (src_db) {
+ assert(src_db->descriptor);
+ assert(src_db->descriptor->dbt.size == 4);
+ assert((*(uint32_t*)src_db->descriptor->dbt.data) == 0);
+ }
+ assert(dest_db->descriptor->dbt.size == 4);
+ uint32_t which = *(uint32_t*)dest_db->descriptor->dbt.data;
+ assert(which < num_dbs);
+
+ if (dest_key->data) toku_free(dest_key->data);
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_key->data = toku_xmemdup (src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ dest_val->data = toku_xmemdup (src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // create a txn that never closes, forcing recovery to run from the beginning of the log
+ {
+ DB_TXN *oldest_living_txn;
+ r = env->txn_begin(env, NULL, &oldest_living_txn, 0); CKERR(r);
+ }
+
+ DBT descriptor;
+ uint32_t which;
+ for (which = 0; which < num_dbs; which++) {
+ dbt_init_realloc(&dest_keys[which]);
+ dbt_init_realloc(&dest_vals[which]);
+ }
+ dbt_init(&descriptor, &which, sizeof(which));
+ DB *dba;
+ DB *dbb;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ which = 0;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dba->change_descriptor(dba, txn_desc, &descriptor, 0); CKERR(chk_r); }
+ });
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ which = 1;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbb->change_descriptor(dbb, txn_desc, &descriptor, 0); CKERR(chk_r); }
+ });
+ DB *dbs[num_dbs] = {dba, dbb};
+ uint32_t flags[num_dbs] = {0, 0};
+ // txn_begin; insert <a,a>; txn_abort
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+ r = env_put_multiple_test_no_array(env, dba, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
+ CKERR(r);
+ r = txn->abort(txn); CKERR(r);
+ }
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(r);
+ dbs[1] = dbb;
+
+ // txn_begin; insert <a,b>;
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+ r = env_put_multiple_test_no_array(env, NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
+ CKERR(r);
+ }
+
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ // abort the process
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
+ // so recovery always runs over the entire log.
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify the data
+ {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ DBT k, v;
+ r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
+ assert(r == DB_NOTFOUND);
+
+ r = cursor->c_close(cursor); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ }
+ {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ DBT k, v;
+ r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
+ assert(r == DB_NOTFOUND);
+
+ r = cursor->c_close(cursor); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ }
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-lsn-filter.cc b/storage/tokudb/PerconaFT/src/tests/recover-lsn-filter.cc
new file mode 100644
index 00000000000..cbb3de61c28
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-lsn-filter.cc
@@ -0,0 +1,190 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test makes sure the LSN filtering is used during recovery
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // create a txn that never closes, forcing recovery to run from the beginning of the log
+ {
+ DB_TXN *oldest_living_txn;
+ r = env->txn_begin(env, NULL, &oldest_living_txn, 0); CKERR(r);
+ }
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ // txn_begin; insert <a,a>; txn_abort
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+ r = db->put(db, txn, &k, &v, 0); CKERR(r);
+ r = txn->abort(txn); CKERR(r);
+ }
+
+ // txn_begin; insert <a,b>;
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+ r = db->put(db, txn, &k, &v, 0); CKERR(r);
+ }
+
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ // abort the process
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
+ // so recovery always runs over the entire log.
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify the data
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ DBT k, v;
+ r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
+ assert(r == DB_NOTFOUND);
+
+ r = cursor->c_close(cursor); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-missing-dbfile-2.cc b/storage/tokudb/PerconaFT/src/tests/recover-missing-dbfile-2.cc
new file mode 100644
index 00000000000..2c0560943d6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-missing-dbfile-2.cc
@@ -0,0 +1,186 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that DB_RUNRECOVERY is returned when there is a missing db file
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+#define NAMEA "a.db"
+const char *namea=NAMEA;
+#define NAMEB "b.db"
+#define NAMEB_HINT "b_db"
+const char *nameb=NAMEB;
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+
+ DB *dbb;
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ char saveddbs[TOKU_PATH_MAX+1];
+ toku_path_join(saveddbs, 2, TOKU_TEST_FILENAME, "saveddbs");
+ toku_os_recursive_delete(saveddbs);
+ r = toku_os_mkdir(saveddbs, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ char glob[TOKU_PATH_MAX+1];
+ toku_path_join(glob, 2, TOKU_TEST_FILENAME, NAMEB_HINT "*.tokudb");
+ char cmd[2 * TOKU_PATH_MAX + sizeof("mv ")];
+ snprintf(cmd, sizeof(cmd), "mv %s %s", glob, saveddbs);
+ r = system(cmd);
+ CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+
+ snprintf(cmd, sizeof(cmd), "rm -rf %s", glob);
+ r = system(cmd);
+ CKERR(r);
+
+ snprintf(cmd, sizeof(cmd), "mv %s/*.tokudb %s", saveddbs, TOKU_TEST_FILENAME);
+ r = system(cmd);
+ CKERR(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-missing-dbfile.cc b/storage/tokudb/PerconaFT/src/tests/recover-missing-dbfile.cc
new file mode 100644
index 00000000000..5ecbc01a999
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-missing-dbfile.cc
@@ -0,0 +1,177 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that DB_RUNRECOVERY is returned when there is a missing db file
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+#define NAMEA "a.db"
+const char *namea=NAMEA;
+#define NAMEB "b.db"
+const char *nameb=NAMEB;
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ }
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ char saveddbs[TOKU_PATH_MAX+1];
+ toku_path_join(saveddbs, 2, TOKU_TEST_FILENAME, "saveddbs");
+ toku_os_recursive_delete(saveddbs);
+ r = toku_os_mkdir(saveddbs, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ char glob[TOKU_PATH_MAX+1];
+ toku_path_join(glob, 2, TOKU_TEST_FILENAME, "*.tokudb");
+ char cmd[2 * TOKU_PATH_MAX + sizeof("mv ")];
+ snprintf(cmd, sizeof(cmd), "mv %s %s", glob, saveddbs);
+ r = system(cmd);
+ CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR2(r, DB_RUNRECOVERY);
+
+ snprintf(cmd, sizeof(cmd), "rm -rf %s", glob);
+ r = system(cmd);
+ CKERR(r);
+
+ snprintf(cmd, sizeof(cmd), "mv %s/*.tokudb %s", saveddbs, TOKU_TEST_FILENAME);
+ r = system(cmd);
+ CKERR(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-missing-logfile.cc b/storage/tokudb/PerconaFT/src/tests/recover-missing-logfile.cc
new file mode 100644
index 00000000000..d65acc41f00
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-missing-logfile.cc
@@ -0,0 +1,182 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that DB_RUNRECOVERY is returned when there is a missing logfile
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void run_test (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ DB_ENV *env;
+ DB *dba;
+
+ // create logfile 0
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ // create logfile 1
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ // create logfile 2
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ }
+
+ r = txn->commit(txn, 0); CKERR(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ char savedlogs[TOKU_PATH_MAX+1];
+ toku_path_join(savedlogs, 2, TOKU_TEST_FILENAME, "savedlogs");
+ toku_os_recursive_delete(savedlogs);
+ r = toku_os_mkdir(savedlogs, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ char glob[TOKU_PATH_MAX+1];
+ toku_path_join(glob, 2, TOKU_TEST_FILENAME, "*.tokulog*");
+ char cmd[2 * TOKU_PATH_MAX + sizeof("mv ")];
+ snprintf(cmd, sizeof(cmd), "mv %s %s", glob, savedlogs);
+ r = system(cmd);
+ CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR2(r, ENOENT);
+
+ snprintf(cmd, sizeof(cmd), "rm -rf %s", glob);
+ r = system(cmd);
+ CKERR(r);
+
+ snprintf(cmd, sizeof(cmd), "mv %s/*.tokulog* %s", savedlogs, TOKU_TEST_FILENAME);
+ r = system(cmd);
+ CKERR(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-abort.cc b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-abort.cc
new file mode 100644
index 00000000000..d045800960c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-abort.cc
@@ -0,0 +1,257 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of a put multiple log entry
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+ assert(src_db == NULL);
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else {
+ dest_val->size = 0;
+ }
+ break;
+ case DB_DBT_REALLOC:
+ assert(0);
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_keys, NULL, src_key, src_data);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0);
+ assert_zero(r);
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ DBT pri_val; dbt_init(&pri_val, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbs]; memset(vals, 0, sizeof vals);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_put_multiple_test_no_array(env, NULL, txn, &pri_key, &pri_val, ndbs, db, keys, vals, flags);
+ assert_zero(r);
+ }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ assert(r == DB_NOTFOUND);
+
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_all(DB_ENV *env, int ndbs) {
+ int r;
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ verify_empty(env, db);
+ r = db->close(db, 0);
+ assert_zero(r);
+ }
+}
+
+static void
+run_recover(int ndbs, int UU(nrows)) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ verify_all(env, ndbs);
+ r = env->close(env, 0); assert_zero(r);
+}
+
+static int
+usage(void) {
+ return 1;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ int ndbs = 2;
+ int nrows = 1;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test)
+ run_test(ndbs, nrows);
+ if (do_recover)
+ run_recover(ndbs, nrows);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-fdelete-all.cc b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-fdelete-all.cc
new file mode 100644
index 00000000000..f7c877c4591
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-fdelete-all.cc
@@ -0,0 +1,232 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test makes sure the LSN filtering is used during recovery of put_multiple
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+enum {num_dbs = 2};
+static DBT dest_keys[num_dbs];
+static DBT dest_vals[num_dbs];
+
+bool do_test=false, do_recover=false;
+
+static int
+put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+
+ assert(src_db == NULL);
+ assert(dest_db->descriptor->dbt.size == 4);
+ uint32_t which = *(uint32_t*)dest_db->descriptor->dbt.data;
+ assert(which < num_dbs);
+
+ if (dest_key->data) toku_free(dest_key->data);
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_key->data = toku_xmemdup (src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ dest_val->data = toku_xmemdup (src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // create a txn that never closes, forcing recovery to run from the beginning of the log
+ {
+ DB_TXN *oldest_living_txn;
+ r = env->txn_begin(env, NULL, &oldest_living_txn, 0); CKERR(r);
+ }
+
+ DBT descriptor;
+ uint32_t which;
+ for (which = 0; which < num_dbs; which++) {
+ dbt_init_realloc(&dest_keys[which]);
+ dbt_init_realloc(&dest_vals[which]);
+ }
+ dbt_init(&descriptor, &which, sizeof(which));
+ DB *dba;
+ DB *dbb;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ which = 0;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dba->change_descriptor(dba, txn_desc, &descriptor, 0); CKERR(chk_r); }
+ });
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ which = 1;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbb->change_descriptor(dbb, txn_desc, &descriptor, 0); CKERR(chk_r); }
+ });
+
+ DB *dbs[num_dbs] = {dba, dbb};
+ uint32_t flags[num_dbs] = {0, 0};
+ // txn_begin; insert <a,a>; txn_abort
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+
+ r = env_put_multiple_test_no_array(env, NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
+ CKERR(r);
+ r = txn->abort(txn); CKERR(r);
+ }
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(r);
+ dbs[1] = dbb;
+
+ // txn_begin; insert <a,b>;
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+
+ r = env_put_multiple_test_no_array(env, NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
+ CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = env->dbremove(env, txn, namea, NULL, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->dbremove(env, txn, nameb, NULL, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+
+ r = env->log_flush(env, NULL); CKERR(r);
+ // abort the process
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
+ // so recovery always runs over the entire log.
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify the data
+ {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR2(r, ENOENT);
+ r = db->close(db, 0); CKERR(r);
+ }
+ {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR2(r, ENOENT);
+ r = db->close(db, 0); CKERR(r);
+ }
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-fdelete-some.cc b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-fdelete-some.cc
new file mode 100644
index 00000000000..9441d93e24f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-fdelete-some.cc
@@ -0,0 +1,251 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test makes sure the LSN filtering is used during recovery of put_multiple
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+enum {num_dbs = 2};
+static DBT dest_keys[num_dbs];
+static DBT dest_vals[num_dbs];
+
+bool do_test=false, do_recover=false;
+
+static int
+put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ if (src_db) {
+ assert(src_db->descriptor);
+ assert(src_db->descriptor->dbt.size == 4);
+ assert((*(uint32_t*)src_db->descriptor->dbt.data) == 0);
+ }
+ assert(dest_db->descriptor->dbt.size == 4);
+ uint32_t which = *(uint32_t*)dest_db->descriptor->dbt.data;
+ assert(which < num_dbs);
+
+ if (dest_key->data) toku_free(dest_key->data);
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_key->data = toku_xmemdup (src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ dest_val->data = toku_xmemdup (src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // create a txn that never closes, forcing recovery to run from the beginning of the log
+ {
+ DB_TXN *oldest_living_txn;
+ r = env->txn_begin(env, NULL, &oldest_living_txn, 0); CKERR(r);
+ }
+
+ DBT descriptor;
+ uint32_t which;
+ for (which = 0; which < num_dbs; which++) {
+ dbt_init_realloc(&dest_keys[which]);
+ dbt_init_realloc(&dest_vals[which]);
+ }
+ dbt_init(&descriptor, &which, sizeof(which));
+ DB *dba;
+ DB *dbb;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ which = 0;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dba->change_descriptor(dba, txn_desc, &descriptor, 0); CKERR(chk_r); }
+ });
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ which = 1;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbb->change_descriptor(dbb, txn_desc, &descriptor, 0); CKERR(chk_r); }
+ });
+
+ DB *dbs[num_dbs] = {dba, dbb};
+ uint32_t flags[num_dbs] = {0, 0};
+ // txn_begin; insert <a,a>; txn_abort
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+
+ r = env_put_multiple_test_no_array(env, dba, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
+ CKERR(r);
+ r = txn->abort(txn); CKERR(r);
+ }
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(r);
+ dbs[1] = dbb;
+
+ // txn_begin; insert <a,b>;
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+
+ r = env_put_multiple_test_no_array(env, NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
+ CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->close(dbb, 0); CKERR(r);
+ r = env->dbremove(env, txn, nameb, NULL, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+
+ r = env->log_flush(env, NULL); CKERR(r);
+ // abort the process
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
+ // so recovery always runs over the entire log.
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify the data
+ {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR2(r, ENOENT);
+ r = db->close(db, 0); CKERR(r);
+ }
+ {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ DBT k, v;
+ r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
+ CKERR(r);
+ assert(k.size == 2);
+ assert(v.size == 2);
+ assert(memcmp(k.data, "a", 2) == 0);
+ assert(memcmp(v.data, "b", 2) == 0);
+ toku_free(k.data);
+ toku_free(v.data);
+
+ r = cursor->c_close(cursor); CKERR(r);
+
+ r = txn->commit(txn, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ }
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-srcdb-fdelete-all.cc b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-srcdb-fdelete-all.cc
new file mode 100644
index 00000000000..9f49e892808
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple-srcdb-fdelete-all.cc
@@ -0,0 +1,233 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// this test makes sure the LSN filtering is used during recovery of put_multiple
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+const char *nameb="b.db";
+enum {num_dbs = 2};
+static DBT dest_keys[num_dbs];
+static DBT dest_vals[num_dbs];
+
+bool do_test=false, do_recover=false;
+
+static int
+put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ assert(src_db != NULL);
+ assert(dest_db->descriptor->dbt.size == 4);
+ uint32_t which = *(uint32_t*)dest_db->descriptor->dbt.data;
+ assert(which < num_dbs);
+
+ if (dest_key->data) toku_free(dest_key->data);
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_key->data = toku_xmemdup (src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ dest_val->data = toku_xmemdup (src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // create a txn that never closes, forcing recovery to run from the beginning of the log
+ {
+ DB_TXN *oldest_living_txn;
+ r = env->txn_begin(env, NULL, &oldest_living_txn, 0); CKERR(r);
+ }
+
+ DBT descriptor;
+ uint32_t which;
+ for (which = 0; which < num_dbs; which++) {
+ dbt_init_realloc(&dest_keys[which]);
+ dbt_init_realloc(&dest_vals[which]);
+ }
+ dbt_init(&descriptor, &which, sizeof(which));
+ DB *dba;
+ DB *dbb;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ which = 0;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dba->change_descriptor(dba, txn_desc, &descriptor, 0); CKERR(chk_r); }
+ });
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ which = 1;
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbb->change_descriptor(dbb, txn_desc, &descriptor, 0); CKERR(chk_r); }
+ });
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ DB *dbs[num_dbs] = {dba, dbb};
+ uint32_t flags[num_dbs] = {0, 0};
+ // txn_begin; insert <a,a>; txn_abort
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+
+ r = env_put_multiple_test_no_array(env, num_dbs > 0 ? dbs[0] : NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
+ CKERR(r);
+ r = txn->abort(txn); CKERR(r);
+ }
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(r);
+ dbs[1] = dbb;
+
+ // txn_begin; insert <a,b>;
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+
+ r = env_put_multiple_test_no_array(env, num_dbs > 0 ? dbs[0] : NULL, txn, &k, &v, num_dbs, dbs, dest_keys, dest_vals, flags);
+ CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = env->dbremove(env, txn, namea, NULL, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->dbremove(env, txn, nameb, NULL, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+
+ r = env->log_flush(env, NULL); CKERR(r);
+ // abort the process
+ toku_hard_crash_on_purpose();
+}
+
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // Recovery starts from oldest_living_txn, which is older than any inserts done in run_test,
+ // so recovery always runs over the entire log.
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify the data
+ {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR2(r, ENOENT);
+ r = db->close(db, 0); CKERR(r);
+ }
+ {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, nameb, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR2(r, ENOENT);
+ r = db->close(db, 0); CKERR(r);
+ }
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-put-multiple.cc b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple.cc
new file mode 100644
index 00000000000..d561286721b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-put-multiple.cc
@@ -0,0 +1,277 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of a put multiple log entry
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+ assert(src_db == NULL);
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else
+ dest_val->size = 0;
+ break;
+ case DB_DBT_REALLOC:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = toku_realloc(dest_val->data, dest_val->size);
+ memcpy(dest_val->data, src_val->data, dest_val->size);
+ } else
+ dest_val->size = 0;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_key_arrays, NULL, src_key, src_data);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0);
+ assert_zero(r);
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT pri_key; dbt_init(&pri_key, &k, sizeof k);
+ DBT pri_val; dbt_init(&pri_val, &v[0], sizeof v);
+ DBT keys[ndbs]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbs]; memset(vals, 0, sizeof vals);
+ uint32_t flags[ndbs]; memset(flags, 0, sizeof flags);
+ r = env_put_multiple_test_no_array(env, NULL, txn, &pri_key, &pri_val, ndbs, db, keys, vals, flags);
+ assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void
+verify_seq(DB_ENV *env, DB *db, int dbnum, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == get_key(i, dbnum));
+
+ if (dbnum == 0) {
+ assert(val.size == ndbs * sizeof (int));
+ int v[ndbs]; get_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ } else
+ assert(val.size == 0);
+ }
+ assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_all(DB_ENV *env, int ndbs, int nrows) {
+ int r;
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ verify_seq(env, db, dbnum, ndbs, nrows);
+ r = db->close(db, 0);
+ assert_zero(r);
+ }
+}
+
+static void
+run_recover(int ndbs, int nrows) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ verify_all(env, ndbs, nrows);
+ r = env->close(env, 0); assert_zero(r);
+}
+
+static int
+usage(void) {
+ return 1;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ int ndbs = 2;
+ int nrows = 1;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test)
+ run_test(ndbs, nrows);
+ if (do_recover)
+ run_recover(ndbs, nrows);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-rollback.cc b/storage/tokudb/PerconaFT/src/tests/recover-rollback.cc
new file mode 100644
index 00000000000..beb37ec3609
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-rollback.cc
@@ -0,0 +1,209 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test dirty upgrade.
+// Generate a rollback log that requires recovery.
+
+#include "test.h"
+
+// Insert max_rows key/val pairs into the db
+static void do_inserts(DB_TXN *txn, DB *db, uint64_t max_rows, size_t val_size) {
+ char val_data[val_size]; memset(val_data, 0, val_size);
+ int r;
+
+ for (uint64_t i = 0; i < max_rows; i++) {
+ // pick a sequential key but it does not matter for this test.
+ uint64_t k[2] = {
+ htonl(i), random64(),
+ };
+
+ DBT key = { .data = k, .size = sizeof k };
+ DBT val = { .data = val_data, .size = (uint32_t) val_size };
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ }
+}
+
+static void run_test(uint64_t num_rows, size_t val_size, bool do_crash) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_cachesize(env, 8, 0, 1);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ do_inserts(txn, db, num_rows, val_size);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ if (do_crash)
+ assert(0); // crash on purpose
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+static void do_verify(DB_ENV *env, DB *db, uint64_t num_rows, size_t val_size UU()) {
+ int r;
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ DBC *c = nullptr;
+ r = db->cursor(db, txn, &c, 0);
+ CKERR(r);
+
+ uint64_t i = 0;
+ while (1) {
+ DBT key = {};
+ DBT val = {};
+ r = c->c_get(c, &key, &val, DB_NEXT);
+ if (r == DB_NOTFOUND)
+ break;
+ CKERR(r);
+ assert(key.size == 16);
+ uint64_t k[2];
+ memcpy(k, key.data, key.size);
+ assert(htonl(k[0]) == i);
+ assert(val.size == val_size);
+ i++;
+ }
+ assert(i == num_rows);
+
+ r = c->c_close(c);
+ CKERR(r);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+}
+
+static void run_recover(uint64_t num_rows, size_t val_size) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_cachesize(env, 8, 0, 1);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ do_verify(env, db, num_rows, val_size);
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char *const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ bool do_crash = true;
+ uint64_t num_rows = 1;
+ size_t val_size = 1;
+
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0) verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(argv[i], "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(argv[i], "--crash") == 0 && i+1 < argc) {
+ do_crash = atoi(argv[++i]);
+ continue;
+ }
+ }
+ if (do_test) {
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ run_test(num_rows, val_size, do_crash);
+ }
+ if (do_recover) {
+ run_recover(num_rows, val_size);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-rollinclude.cc b/storage/tokudb/PerconaFT/src/tests/recover-rollinclude.cc
new file mode 100644
index 00000000000..6a847af7b09
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-rollinclude.cc
@@ -0,0 +1,221 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Create a rollback log with a rollinclude log entry, crash after the txn commits and before the last checkpoint.
+// Recovery crashes 7.1.0, should succeed.
+
+#include "test.h"
+
+// Insert max_rows key/val pairs into the db
+
+// We want to force a rollinclude so we use a child transaction and insert enough rows so that it spills.
+// It spills at about 144K and 289K rows.
+static void do_inserts(DB_ENV *env, DB *db, uint64_t max_rows, size_t val_size) {
+ char val_data[val_size]; memset(val_data, 0, val_size);
+ int r;
+ DB_TXN *parent = nullptr;
+ r = env->txn_begin(env, nullptr, &parent, 0);
+ CKERR(r);
+
+ DB_TXN *child = nullptr;
+ r = env->txn_begin(env, parent, &child, 0);
+ CKERR(r);
+
+ for (uint64_t i = 0; i < max_rows; i++) {
+ // pick a sequential key but it does not matter for this test.
+ uint64_t k[2] = {
+ htonl(i), random64(),
+ };
+
+ DBT key = { .data = k, .size = sizeof k };
+ DBT val = { .data = val_data, .size = (uint32_t) val_size };
+ r = db->put(db, child, &key, &val, 0);
+ CKERR(r);
+
+ if (i == max_rows-1) {
+ r = child->commit(child, 0);
+ CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+ }
+ }
+
+ r = parent->commit(parent, 0);
+ CKERR(r);
+}
+
+static void run_test(uint64_t num_rows, size_t val_size, bool do_crash) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_cachesize(env, 8, 0, 1);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ do_inserts(env, db, num_rows, val_size);
+
+ if (do_crash)
+ assert(0); // crash on purpose
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+static void do_verify(DB_ENV *env, DB *db, uint64_t num_rows, size_t val_size UU()) {
+ int r;
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ DBC *c = nullptr;
+ r = db->cursor(db, txn, &c, 0);
+ CKERR(r);
+
+ uint64_t i = 0;
+ while (1) {
+ DBT key = {};
+ DBT val = {};
+ r = c->c_get(c, &key, &val, DB_NEXT);
+ if (r == DB_NOTFOUND)
+ break;
+ CKERR(r);
+ assert(key.size == 16);
+ uint64_t k[2];
+ memcpy(k, key.data, key.size);
+ assert(htonl(k[0]) == i);
+ assert(val.size == val_size);
+ i++;
+ }
+ assert(i == num_rows);
+
+ r = c->c_close(c);
+ CKERR(r);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+}
+
+static void run_recover(uint64_t num_rows, size_t val_size) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_cachesize(env, 8, 0, 1);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ do_verify(env, db, num_rows, val_size);
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char *const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ bool do_crash = true;
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0) verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(argv[i], "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(argv[i], "--crash") == 0 && i+1 < argc) {
+ do_crash = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ uint64_t num_rows = 300000;
+ size_t val_size = 1;
+
+ if (do_test) {
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ run_test(num_rows, val_size, do_crash);
+ }
+ if (do_recover) {
+ run_recover(num_rows, val_size);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-split-checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-split-checkpoint.cc
new file mode 100644
index 00000000000..b53e6752f9e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-split-checkpoint.cc
@@ -0,0 +1,198 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// force a checkpoint to span multiple tokulog files. in other words, the begin checkpoint log entry and the
+// end checkpoint log entry for the same checkpoint are in different log files.
+
+#include <sys/stat.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static void test_checkpoint_callback(void *extra) {
+ int r;
+ DB_ENV *env = (DB_ENV *) extra;
+
+ // create and commit a bunch of transactions. the last commit fsync's the log. since the log is
+ // really small, a new log file is created before the end checkpoint is logged.
+ int i;
+ for (i=0; i<100; i++) {
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, i == 99 ? DB_TXN_SYNC : 0); CKERR(r);
+ }
+}
+
+static void test_checkpoint_callback2(void *extra) {
+ (void) extra;
+}
+
+static void run_test (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); CKERR(r);
+
+ db_env_set_checkpoint_callback(test_checkpoint_callback, env);
+ db_env_set_checkpoint_callback2(test_checkpoint_callback2, env);
+
+ r = env->set_lg_max(env, 1024); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (bool did_commit) {
+ (void) did_commit;
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void run_recover_only (void) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void run_no_recover (void) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+}
+
+const char *cmd;
+
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_commit) {
+ run_test(true, false);
+ } else if (do_abort) {
+ run_test(false, true);
+ } else if (do_recover_committed) {
+ run_recover(true);
+ } else if (do_recover_aborted) {
+ run_recover(false);
+ } else if (do_recover_only) {
+ run_recover_only();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-straddle-txn-nested.cc b/storage/tokudb/PerconaFT/src/tests/recover-straddle-txn-nested.cc
new file mode 100644
index 00000000000..18818c73fb7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-straddle-txn-nested.cc
@@ -0,0 +1,172 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// TODO
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ DB_TXN *atxn;
+ r = env->txn_begin(env, NULL, &atxn, 0); CKERR(r);
+
+ // create a live transaction so that recovery needs to come here
+ DB_TXN *livetxn;
+ r = env->txn_begin(env, NULL, &livetxn, 0); CKERR(r);
+
+ DB_TXN *btxn;
+ r = env->txn_begin(env, atxn, &btxn, 0); CKERR(r);
+
+ r = btxn->commit(btxn, 0); CKERR(r);
+
+ r = atxn->commit(atxn, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ // abort the process
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify the data
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ DBT k, v;
+ r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
+ assert(r == DB_NOTFOUND);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-straddle-txn.cc b/storage/tokudb/PerconaFT/src/tests/recover-straddle-txn.cc
new file mode 100644
index 00000000000..163724a07bf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-straddle-txn.cc
@@ -0,0 +1,176 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// nested txn straddles the recovery turn around point in the log
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+const char *namea="a.db";
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ DB_TXN *atxn;
+ r = env->txn_begin(env, NULL, &atxn, 0); CKERR(r);
+
+ DB_TXN *btxn;
+ r = env->txn_begin(env, atxn, &btxn, 0); CKERR(r);
+
+ // create a live transaction so that recovery needs to come here
+ DB_TXN *livetxn;
+ r = env->txn_begin(env, NULL, &livetxn, 0); CKERR(r);
+
+ DBT k,v;
+ dbt_init(&k, "a", 2);
+ dbt_init(&v, "b", 2);
+ r = db->put(db, btxn, &k, &v, 0); CKERR(r);
+ r = btxn->commit(btxn, 0); CKERR(r);
+
+ r = atxn->abort(atxn); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ // abort the process
+ toku_hard_crash_on_purpose();
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // verify the data
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, namea, NULL, DB_UNKNOWN, DB_AUTO_COMMIT, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ DBT k, v;
+ r = cursor->c_get(cursor, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST);
+ assert(r == DB_NOTFOUND);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-tablelock.cc b/storage/tokudb/PerconaFT/src/tests/recover-tablelock.cc
new file mode 100644
index 00000000000..30b616899db
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-tablelock.cc
@@ -0,0 +1,239 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the table lock log entry is handled
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ r = db->pre_acquire_table_lock(db, txn); CKERR(r);
+
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = db->put(db, txn, &a, &b, 0); CKERR(r);
+
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool UU(did_commit)) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DBC *ca;
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ DBT aa,ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ int r;
+ DB_ENV *env;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_no_recover (void) {
+ int r;
+ DB_ENV *env;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ } else if (do_no_recover) {
+ do_x1_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test-logsuppress-put.cc b/storage/tokudb/PerconaFT/src/tests/recover-test-logsuppress-put.cc
new file mode 100644
index 00000000000..4fccb003b48
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test-logsuppress-put.cc
@@ -0,0 +1,290 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Verify that log-suppress recovery is done properly. (See ticket 2781.)
+// TODO: determine if this is useful at all anymore (log suppression does not exist anymore)
+
+
+#include <sys/stat.h>
+#include <db.h>
+#include "test.h"
+#include "ydb-internal.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+
+DB_ENV *env;
+DB_TXN *parent;
+DB_TXN *child;
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Some loader functions to allow test to work:
+
+
+// There is no handlerton in this test, so this function is a local replacement
+// for the handlerton's generate_row_for_put().
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+
+ (void) src_db;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ if ( which == 0 ) {
+ if (dest_key->flags==DB_DBT_REALLOC) {
+ if (dest_key->data) toku_free(dest_key->data);
+ dest_key->flags = 0;
+ dest_key->ulen = 0;
+ }
+ if (dest_val->flags==DB_DBT_REALLOC) {
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_val->flags = 0;
+ dest_val->ulen = 0;
+ }
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+ }
+ else {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(unsigned int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(unsigned int));
+ dest_key->ulen = sizeof(unsigned int);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < sizeof(unsigned int)) {
+ dest_val->data = toku_xrealloc(dest_val->data, sizeof(unsigned int));
+ dest_val->ulen = sizeof(unsigned int);
+ }
+ unsigned int *new_key = (unsigned int *)dest_key->data;
+ unsigned int *new_val = (unsigned int *)dest_val->data;
+
+ *new_key = 1;
+ *new_val = 2;
+
+ dest_key->size = sizeof(unsigned int);
+ dest_val->size = sizeof(unsigned int);
+ //data is already set above
+ }
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// The test itself:
+
+#define MAX_DBS 1
+#define NUM_ROWS 1
+#define NUM_DBS 1
+
+// Create loader, insert row(s)
+static void
+load(DB **dbs) {
+ int r;
+ DB_TXN *txn;
+ DB_LOADER *loader;
+ uint32_t db_flags[MAX_DBS];
+ uint32_t dbt_flags[MAX_DBS];
+ for(int i=0;i<MAX_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = LOADER_COMPRESS_INTERMEDIATES;
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+
+ // using loader->put, put values into DB
+ DBT key, val;
+ unsigned int k, v;
+ for(int i=1;i<=NUM_ROWS;i++) {
+ k = i;
+ v = i+1;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ CKERR(r);
+ }
+
+}
+
+
+static void
+checkpoint_callback(void * UU(extra)){
+ printf("Deliberately crash during checkpoint\n");
+ fflush(stdout);
+ int r = env->log_flush(env, NULL); //TODO: USe a real DB_LSN* instead of NULL
+ CKERR(r);
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_shutdown (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+
+ char name[128];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ load(dbs);
+
+ // r=env->txn_begin(env, 0, &parent, 0); assert(r==0);
+ // r=env->txn_begin(env, &parent, &child, 0); assert(r==0);
+
+ // crash during checkpoint
+ db_env_set_checkpoint_callback(checkpoint_callback, NULL);
+ r=env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+}
+
+static void
+do_x1_recover (bool UU(did_commit)) {
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ /*****
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db->get(db, tid, dbt_init(&key, "a", 2), dbt_init_malloc(&data), 0); assert(r==0);
+ r=tid->commit(tid, 0); assert(r==0);
+ toku_free(data.data);
+ r=db->close(db, 0); CKERR(r);
+ *********/
+ r=env->close(env, 0); CKERR(r);
+}
+
+bool do_commit=false, do_recover_committed=false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown();
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test-logsuppress.cc b/storage/tokudb/PerconaFT/src/tests/recover-test-logsuppress.cc
new file mode 100644
index 00000000000..36f14c4cb23
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test-logsuppress.cc
@@ -0,0 +1,287 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Verify that log-suppress recovery is done properly. (See ticket 2781.)
+
+
+#include <sys/stat.h>
+#include <db.h>
+#include "test.h"
+#include "ydb-internal.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+
+DB_ENV *env;
+DB_TXN *parent;
+DB_TXN *child;
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Some loader functions to allow test to work:
+
+
+// There is no handlerton in this test, so this function is a local replacement
+// for the handlerton's generate_row_for_put().
+static int put_multiple_generate(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_keys, 1);
+ toku_dbt_array_resize(dest_vals, 1);
+ DBT *dest_key = &dest_keys->dbts[0];
+ DBT *dest_val = &dest_vals->dbts[0];
+ (void) src_db;
+ (void) src_key;
+ (void) src_val;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ assert(which != 0);
+ assert(dest_db != src_db);
+ {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(unsigned int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(unsigned int));
+ dest_key->ulen = sizeof(unsigned int);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < sizeof(unsigned int)) {
+ dest_val->data = toku_xrealloc(dest_val->data, sizeof(unsigned int));
+ dest_val->ulen = sizeof(unsigned int);
+ }
+ unsigned int *new_key = (unsigned int *)dest_key->data;
+ unsigned int *new_val = (unsigned int *)dest_val->data;
+
+ *new_key = 1;
+ *new_val = 2;
+
+ dest_key->size = sizeof(unsigned int);
+ dest_val->size = sizeof(unsigned int);
+ //data is already set above
+ }
+
+// printf("dest_key.data = %d\n", *(int*)dest_key->data);
+// printf("dest_val.data = %d\n", *(int*)dest_val->data);
+
+ return 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// The test itself:
+
+#define MAX_DBS 1
+#define NUM_ROWS 1
+#define NUM_DBS 1
+
+// Create loader, insert row(s)
+static void
+load(DB **dbs) {
+ int r;
+ DB_TXN *ptxn = NULL;
+ DB_TXN *txn = NULL;
+ DB_LOADER *loader;
+ uint32_t db_flags[MAX_DBS];
+ uint32_t dbt_flags[MAX_DBS];
+ for(int i=0;i<MAX_DBS;i++) {
+ db_flags[i] = DB_NOOVERWRITE;
+ dbt_flags[i] = 0;
+ }
+ uint32_t loader_flags = 0;
+
+ // create and initialize loader
+ r = env->txn_begin(env, NULL, &ptxn, 0);
+ CKERR(r);
+ r = env->txn_begin(env, ptxn, &txn, 0);
+ CKERR(r);
+ r = env->create_loader(env, txn, &loader, dbs[0], NUM_DBS, dbs, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+
+ // using loader->put, put values into DB
+ DBT key, val;
+ unsigned int k, v;
+ for(int i=1;i<=NUM_ROWS;i++) {
+ k = i;
+ v = i+1;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ r = loader->put(loader, &key, &val);
+ CKERR(r);
+ }
+
+ // close loader
+ r = loader->close(loader);
+ CKERR(r);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+}
+
+
+static void
+checkpoint_callback(void * UU(extra)){
+ printf("Deliberately crash during checkpoint\n");
+ fflush(stdout);
+ int r = env->log_flush(env, NULL); //TODO: USe a real DB_LSN* instead of NULL
+ CKERR(r);
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_shutdown (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+
+ char name[128];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ load(dbs);
+
+ // r=env->txn_begin(env, 0, &parent, 0); assert(r==0);
+ // r=env->txn_begin(env, &parent, &child, 0); assert(r==0);
+
+ // crash during checkpoint
+ db_env_set_checkpoint_callback(checkpoint_callback, NULL);
+ r=env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+}
+
+static void
+do_x1_recover (bool UU(did_commit)) {
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ /*****
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db->get(db, tid, dbt_init(&key, "a", 2), dbt_init_malloc(&data), 0); assert(r==0);
+ r=tid->commit(tid, 0); assert(r==0);
+ toku_free(data.data);
+ r=db->close(db, 0); CKERR(r);
+ *********/
+ r=env->close(env, 0); CKERR(r);
+}
+
+bool do_commit=false, do_recover_committed=false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown();
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test1.cc b/storage/tokudb/PerconaFT/src/tests/recover-test1.cc
new file mode 100644
index 00000000000..1b159e2198d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test1.cc
@@ -0,0 +1,160 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the table lock log entry is handled
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+
+DB_ENV *env;
+DB_TXN *tid;
+DB *db;
+DBT key,data;
+
+static void
+do_x1_shutdown (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ {
+ DB_TXN *oldest;
+ r=env->txn_begin(env, 0, &oldest, 0);
+ CKERR(r);
+ }
+
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->put(db, tid, dbt_init(&key, "a", 2), dbt_init(&data, "b", 2), 0); assert(r==0);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=db->close(db, 0); assert(r==0);
+
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool UU(did_commit)) {
+ int r;
+ char glob[TOKU_PATH_MAX+1];
+ toku_os_recursive_delete(toku_path_join(glob, 2, TOKU_TEST_FILENAME, "*.tokudb"));
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db->get(db, tid, dbt_init(&key, "a", 2), dbt_init_malloc(&data), 0); assert(r==0);
+ r=tid->commit(tid, 0); assert(r==0);
+ toku_free(data.data);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+bool do_commit=false, do_recover_committed=false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown();
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test2.cc b/storage/tokudb/PerconaFT/src/tests/recover-test2.cc
new file mode 100644
index 00000000000..167c8f5888f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test2.cc
@@ -0,0 +1,180 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the table lock log entry is handled
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const char *namea="a.db";
+
+DB_ENV *env;
+DB_TXN *tid;
+DB *db;
+DBT key,data;
+int i;
+enum {N=1000};
+char *keys[N];
+char *vals[N];
+
+static void
+do_x1_shutdown (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ {
+ DB_TXN *oldest;
+ r=env->txn_begin(env, 0, &oldest, 0);
+ CKERR(r);
+ }
+
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ for (i=0; i<N; i++) {
+ r=db->put(db, tid, dbt_init(&key, keys[i], strlen(keys[i])+1), dbt_init(&data, vals[i], strlen(vals[i])+1), 0); assert(r==0);
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=db->close(db, 0); assert(r==0);
+
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool UU(did_commit)) {
+ int r;
+ char glob[TOKU_PATH_MAX+1];
+ toku_os_recursive_delete(toku_path_join(glob, 2, TOKU_TEST_FILENAME, "*.tokudb"));
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ for (i=0; i<N; i++) {
+ r=db->get(db, tid, dbt_init(&key, keys[i], 1+strlen(keys[i])), dbt_init_malloc(&data), 0); assert(r==0);
+ assert(strcmp((char*)data.data, vals[i])==0);
+ toku_free(data.data);
+ data.data=0;
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ toku_free(data.data);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+bool do_commit=false, do_recover_committed=false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ srandom(0xDEADBEEF);
+ for (i=0; i<N; i++) {
+ char ks[100]; snprintf(ks, sizeof(ks), "k%09ld.%d", random(), i);
+ char vs[1000]; snprintf(vs, sizeof(vs), "v%d.%0*d", i, (int)(sizeof(vs)-100), i);
+ keys[i]=toku_strdup(ks);
+ vals[i]=toku_strdup(vs);
+ }
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown();
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ }
+ for (i=0; i<N; i++) {
+ toku_free(keys[i]);
+ toku_free(vals[i]);
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test3.cc b/storage/tokudb/PerconaFT/src/tests/recover-test3.cc
new file mode 100644
index 00000000000..9123c6db60d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test3.cc
@@ -0,0 +1,188 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the table lock log entry is handled
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const char *namea="a.db";
+
+DB_ENV *env;
+DB_TXN *tid;
+DB *db;
+DBT key,data;
+int i;
+enum {N=10000};
+char *keys[N];
+char *vals[N];
+
+static void
+do_x1_shutdown (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ {
+ DB_TXN *oldest;
+ r=env->txn_begin(env, 0, &oldest, 0);
+ CKERR(r);
+ }
+
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ for (i=0; i<N; i++) {
+ r=db->put(db, tid, dbt_init(&key, keys[i], strlen(keys[i])+1), dbt_init(&data, vals[i], strlen(vals[i])+1), 0); assert(r==0);
+ if (i%500==499) {
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ }
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=db->close(db, 0); assert(r==0);
+
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool UU(did_commit)) {
+ int r;
+ char glob[TOKU_PATH_MAX+1];
+ toku_os_recursive_delete(toku_path_join(glob, 2, TOKU_TEST_FILENAME, "*.tokudb"));
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ for (i=0; i<N; i++) {
+ r=db->get(db, tid, dbt_init(&key, keys[i], 1+strlen(keys[i])), dbt_init_malloc(&data), 0); assert(r==0);
+ assert(strcmp((char*)data.data, vals[i])==0);
+ toku_free(data.data);
+ data.data=0;
+ if (i%500==499) {
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ }
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ toku_free(data.data);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+bool do_commit=false, do_recover_committed=false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ srandom(0xDEADBEEF);
+ for (i=0; i<N; i++) {
+ char ks[100]; snprintf(ks, sizeof(ks), "k%09ld.%d", random(), i);
+ char vs[1000]; snprintf(vs, sizeof(vs), "v%d.%0*d", i, (int)(sizeof(vs)-100), i);
+ keys[i]=toku_strdup(ks);
+ vals[i]=toku_strdup(vs);
+ }
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown();
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ }
+ for (i=0; i<N; i++) {
+ toku_free(keys[i]);
+ toku_free(vals[i]);
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test_crash_in_flusher_thread.h b/storage/tokudb/PerconaFT/src/tests/recover-test_crash_in_flusher_thread.h
new file mode 100644
index 00000000000..0ed68207ed4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test_crash_in_flusher_thread.h
@@ -0,0 +1,135 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+toku_pthread_t checkpoint_tid;
+static int cnt = 0;
+static bool starting_a_chkpt = false;
+
+int state_to_crash = 0;
+
+static void *do_checkpoint_and_crash(void *arg) {
+ // first verify that checkpointed_data is correct;
+ DB_ENV* CAST_FROM_VOIDP(env, arg);
+ if (verbose) printf("starting a checkpoint\n");
+ int r = env->txn_checkpoint(env, 0, 0, 0); assert(r==0);
+ if (verbose) printf("completed a checkpoint, about to crash\n");
+ toku_hard_crash_on_purpose();
+ return arg;
+}
+
+static void flt_callback(int flt_state, void* extra) {
+ cnt++;
+ if (verbose) printf("flt_state!! %d\n", flt_state);
+ if (cnt > 0 && !starting_a_chkpt && flt_state == state_to_crash) {
+ starting_a_chkpt = true;
+ if (verbose) printf("flt_state %d\n", flt_state);
+ int r = toku_pthread_create(&checkpoint_tid, NULL, do_checkpoint_and_crash, extra);
+ assert(r==0);
+ usleep(2*1000*1000);
+ }
+}
+
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - one thread constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - one thread doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 1;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+
+ // make the guy that updates the db
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ myargs[0].operation_extra = &uoe;
+ myargs[0].operation = update_op;
+ //myargs[0].update_pad_frequency = 0;
+
+ db_env_set_flusher_thread_callback(flt_callback, env);
+ run_workers(myargs, num_threads, cli_args->num_seconds, true, cli_args);
+}
+
+static int
+run_recover_flt_test(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ // make test time arbitrarily high because we expect a crash
+ args.num_seconds = 1000000000;
+ if (state_to_crash == 1) {
+ // Getting flt_state 1 (inbox flush) requires a larger tree with more messages floating in it
+ args.num_elements = 100000;
+ args.disperse_keys = true;
+ args.key_size = 8;
+ args.val_size = 192;
+ } else {
+ args.num_elements = 2000;
+ }
+ // we want to induce a checkpoint
+ args.env_args.checkpointing_period = 0;
+ args.env_args.cachetable_size = 20 * 1024 * 1024;
+ parse_stress_test_args(argc, argv, &args);
+ if (args.do_test_and_crash) {
+ stress_test_main(&args);
+ }
+ if (args.do_recover) {
+ stress_recover(&args);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test_stress1.cc b/storage/tokudb/PerconaFT/src/tests/recover-test_stress1.cc
new file mode 100644
index 00000000000..91ad596dc4d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test_stress1.cc
@@ -0,0 +1,151 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - Grow the dictionary with insertions
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success. It also tests that snapshots
+// work correctly by verifying that table scans sum their vals to 0.
+//
+// This does NOT test:
+// - splits and merges
+// - multiple DBs
+//
+// Variables that are interesting to tweak and run:
+// - small cachetable
+// - number of elements
+//
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - one thread constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - one thread doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 4 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ struct scan_op_extra soe[4];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op;
+
+ // make the backward fast scanner
+ soe[2].fast = true;
+ soe[2].fwd = false;
+ soe[2].prefetch = false;
+ myargs[2].operation_extra = &soe[2];
+ myargs[2].operation = scan_op;
+
+ // make the backward slow scanner
+ soe[3].fast = false;
+ soe[3].fwd = false;
+ soe[3].prefetch = false;
+ myargs[3].operation_extra = &soe[3];
+ myargs[3].operation = scan_op;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 4; i < 4 + cli_args->num_update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ myargs[i].do_prepare = true;
+ }
+
+ // make the guy that does point queries
+ for (int i = 4 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].operation = ptquery_op;
+ myargs[i].do_prepare = true;
+ }
+
+ int num_seconds = random() % cli_args->num_seconds;
+ run_workers(myargs, num_threads, num_seconds, true, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ args.env_args.checkpointing_period = 1;
+ parse_stress_test_args(argc, argv, &args);
+ if (args.do_test_and_crash) {
+ stress_test_main(&args);
+ }
+ if (args.do_recover) {
+ stress_recover(&args);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test_stress2.cc b/storage/tokudb/PerconaFT/src/tests/recover-test_stress2.cc
new file mode 100644
index 00000000000..006b96ce965
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test_stress2.cc
@@ -0,0 +1,84 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_update_threads;
+ struct arg myargs[num_threads];
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 0; i < 0 + cli_args->num_update_threads; ++i) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ }
+
+
+ int num_seconds = random() % cli_args->num_seconds;
+ run_workers(myargs, num_threads, num_seconds, true, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ args.env_args.checkpointing_period = 1;
+ args.num_elements = 2000;
+ parse_stress_test_args(argc, argv, &args);
+ if (args.do_test_and_crash) {
+ stress_test_main(&args);
+ }
+ if (args.do_recover) {
+ stress_recover(&args);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test_stress3.cc b/storage/tokudb/PerconaFT/src/tests/recover-test_stress3.cc
new file mode 100644
index 00000000000..066a74da60d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test_stress3.cc
@@ -0,0 +1,180 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - Grow the dictionary with insertions
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success. It also tests that snapshots
+// work correctly by verifying that table scans sum their vals to 0.
+//
+// This does NOT test:
+// - splits and merges
+// - multiple DBs
+//
+// Variables that are interesting to tweak and run:
+// - small cachetable
+// - number of elements
+//
+
+uint64_t time_til_crash;
+uint64_t start_time;
+
+static uint64_t get_tnow(void) {
+ struct timeval tv;
+ int r = gettimeofday(&tv, NULL); assert(r == 0);
+ return tv.tv_sec * 1000000ULL + tv.tv_usec;
+}
+
+static void checkpoint_callback2(void* UU(extra)) {
+ uint64_t curr_time = get_tnow();
+ uint64_t time_diff = curr_time - start_time;
+ if ((time_diff/1000000ULL) > time_til_crash) {
+ toku_hard_crash_on_purpose();
+ }
+}
+
+static int manual_checkpoint(DB_TXN *UU(txn), ARG UU(arg), void* operation_extra, void *UU(stats_extra)) {
+ DB_ENV* CAST_FROM_VOIDP(env, operation_extra);
+ int r = env->txn_checkpoint(env,0,0,0);
+ assert_zero(r);
+ return 0;
+}
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - one thread constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - one thread doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 5 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ struct scan_op_extra soe[4];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op;
+
+ // make the backward fast scanner
+ soe[2].fast = true;
+ soe[2].fwd = false;
+ soe[2].prefetch = false;
+ myargs[2].operation_extra = &soe[2];
+ myargs[2].operation = scan_op;
+
+ // make the backward slow scanner
+ soe[3].fast = false;
+ soe[3].fwd = false;
+ soe[3].prefetch = false;
+ myargs[3].operation_extra = &soe[3];
+ myargs[3].operation = scan_op;
+
+ // make something for checkpoints
+ myargs[4].operation = manual_checkpoint;
+ myargs[4].sleep_ms = 30*1000; // do checkpoints every 30 seconds
+ myargs[4].operation_extra = env;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 5; i < 5 + cli_args->num_update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ }
+
+ // make the guy that does point queries
+ for (int i = 5 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].operation = ptquery_op;
+ }
+
+ db_env_set_checkpoint_callback2(checkpoint_callback2, NULL);
+ time_til_crash = random() % cli_args->num_seconds;
+ start_time = get_tnow();
+ run_workers(myargs, num_threads, INT32_MAX, true, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ args.env_args.checkpointing_period = 0;
+ parse_stress_test_args(argc, argv, &args);
+ if (args.do_test_and_crash) {
+ stress_test_main(&args);
+ }
+ if (args.do_recover) {
+ stress_recover(&args);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-test_stress_openclose.cc b/storage/tokudb/PerconaFT/src/tests/recover-test_stress_openclose.cc
new file mode 100644
index 00000000000..217c3e02cf1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-test_stress_openclose.cc
@@ -0,0 +1,63 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "stress_openclose.h"
+
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ parse_stress_test_args(argc, argv, &args);
+ // checkpointing is a part of the ref count, so do it often
+ args.env_args.checkpointing_period = 5;
+ // very small dbs, so verification scans are short and sweet
+ args.num_elements = 1000;
+ // it's okay for update to get DB_LOCK_NOTGRANTED, etc.
+ args.crash_on_operation_failure = false;
+
+ // set crash at end to true for the recovery version
+ // then run the test or run recovery, depending on args
+ stress_openclose_crash_at_end = true;
+ if (args.do_test_and_crash) {
+ stress_test_main(&args);
+ }
+ if (args.do_recover) {
+ stress_recover(&args);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update-multiple-abort.cc b/storage/tokudb/PerconaFT/src/tests/recover-update-multiple-abort.cc
new file mode 100644
index 00000000000..67ff99b09c9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update-multiple-abort.cc
@@ -0,0 +1,497 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of some update multiple operations
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static int
+get_num_new_keys(int i, int dbnum) {
+ if (dbnum == 0) return 1;
+ if (i & (1<<4)) {
+ dbnum++; // Shift every once in a while.
+ }
+ return (i + dbnum) % 3; // 0, 1, or 2
+}
+
+static int
+get_num_keys(int i, int dbnum) {
+ if (dbnum == 0) return 1;
+ return (i + dbnum) % 3; // 0, 1, or 2
+}
+
+static int
+get_total_secondary_rows(int num_primary) {
+ assert(num_primary % 3 == 0);
+ return num_primary / 3 * (0 + 1 + 2);
+}
+
+static int
+get_total_num_keys(int i, int num_dbs) {
+ int sum = 0;
+ for (int db = 1; db < num_dbs; ++db) {
+ sum += get_num_keys(i, db);
+ }
+ return sum;
+}
+
+static int
+get_total_num_new_keys(int i, int num_dbs) {
+ int sum = 0;
+ for (int db = 1; db < num_dbs; ++db) {
+ sum += get_num_new_keys(i, db);
+ }
+ return sum;
+}
+
+static int
+get_key(int i, int dbnum, int which) {
+ assert(i < INT16_MAX / 2);
+ assert(which >= 0);
+ assert(which < get_num_keys(i, dbnum));
+ assert(which < 4);
+ assert(dbnum < 16);
+ if (dbnum == 0) {
+ assert(which == 0);
+ return htonl((2*i) << 16);
+ } else {
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1));
+ }
+}
+
+static int
+get_new_key(int i, int dbnum, int which) {
+ assert(which >= 0);
+ assert(which < get_num_new_keys(i, dbnum));
+ assert(which < 4);
+ assert(dbnum < 16);
+
+ if (dbnum == 0) {
+ assert(which == 0);
+ return htonl((2*i+1) << 16);
+ } else if ((i+dbnum+which) & (1<<5)) {
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1)); // no change from original
+ } else {
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1) + 1);
+ }
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ int index = 0;
+ for (int dbnum = 1; dbnum < ndbs; dbnum++) {
+ for (int which = 0; which < get_num_keys(i, dbnum); ++which) {
+ v[index++] = get_key(i, dbnum, which);
+ }
+ }
+}
+
+static void
+get_new_data(int *v, int i, int ndbs) {
+ int index = 0;
+ for (int dbnum = 1; dbnum < ndbs; dbnum++) {
+ for (int which = 0; which < get_num_new_keys(i, dbnum); ++which) {
+ v[index++] = get_new_key(i, dbnum, which);
+ if (which > 0) {
+ assert(index >= 2);
+ assert(memcmp(&v[index-2], &v[index-1], sizeof(v[0])) < 0);
+ }
+ }
+ }
+}
+
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ (void)src_val;
+ assert(src_db != dest_db);
+ assert(src_db);
+ int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum > 0);
+
+ int pri_key = *(int *) src_key->data;
+ int* pri_val = (int*) src_val->data;
+
+ bool is_new = (ntohl(pri_key) >> 16) % 2 == 1;
+ int i = (ntohl(pri_key) >> 16) / 2;
+
+ int num_keys = is_new ? get_num_new_keys(i, dbnum) : get_num_keys(i, dbnum);
+
+ toku_dbt_array_resize(dest_key_arrays, num_keys);
+
+ if (dest_val_arrays) {
+ toku_dbt_array_resize(dest_val_arrays, num_keys);
+ }
+
+ int index = 0;
+
+ for (int idb = 1; idb < dbnum; idb++) {
+ index += is_new ? get_num_new_keys(i, idb) : get_num_keys(i, idb);
+ }
+ assert(src_val->size % sizeof(int) == 0);
+ assert((int)src_val->size / 4 >= index + num_keys);
+
+ for (int which = 0; which < num_keys; which++) {
+ DBT *dest_key = &dest_key_arrays->dbts[which];
+ DBT *dest_val = NULL;
+
+ assert(dest_key->flags == DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(int));
+ dest_key->ulen = sizeof(int);
+ }
+ dest_key->size = sizeof(int);
+ if (dest_val_arrays) {
+ dest_val = &dest_val_arrays->dbts[which];
+ assert(dest_val->flags == DB_DBT_REALLOC);
+ dest_val->size = 0;
+ }
+ int new_key = is_new ? get_new_key(i, dbnum, which) : get_key(i, dbnum, which);
+ assert(new_key == pri_val[index + which]);
+ *(int*)dest_key->data = new_key;
+ }
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_keys, NULL, src_key, src_data);
+}
+
+static void
+update_diagonal(DB_ENV *env, DB_TXN *txn, DB *db[], int ndbs, int nrows) {
+ assert(ndbs > 0);
+ int r;
+
+ int narrays = 2 * ndbs;
+ DBT_ARRAY keys[narrays];
+ DBT_ARRAY vals[narrays];
+ for (int i = 0; i < narrays; i++) {
+ toku_dbt_array_init(&keys[i], 1);
+ toku_dbt_array_init(&vals[i], 1);
+ }
+
+ for (int i = 0; i < nrows; i++) {
+
+ // update the data i % ndbs col from x to x+1
+
+ int old_k = get_key(i, 0, 0);
+ DBT old_key; dbt_init(&old_key, &old_k, sizeof old_k);
+ int new_k = get_new_key(i, 0, 0);
+ DBT new_key; dbt_init(&new_key, &new_k, sizeof new_k);
+
+ int num_old_keys = get_total_num_keys(i, ndbs);
+ int v[num_old_keys]; get_data(v, i, ndbs);
+ DBT old_data; dbt_init(&old_data, &v[0], sizeof v);
+
+ int num_new_keys = get_total_num_new_keys(i, ndbs);
+ int newv[num_new_keys]; get_new_data(newv, i, ndbs);
+ DBT new_data; dbt_init(&new_data, &newv[0], sizeof newv);
+
+ uint32_t flags_array[ndbs]; memset(flags_array, 0, sizeof(flags_array));
+
+ r = env->update_multiple(env, db[0], txn, &old_key, &old_data, &new_key, &new_data, ndbs, db, flags_array, narrays, keys, narrays, vals);
+ assert_zero(r);
+ }
+ for (int i = 0; i < narrays; i++) {
+ toku_dbt_array_destroy(&keys[i]);
+ toku_dbt_array_destroy(&vals[i]);
+ }
+
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0, 0);
+ int secondary_keys = get_total_num_keys(i, ndbs);
+ int v[secondary_keys]; get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ for (int which = 0; which < get_num_keys(i, dbnum); which++) {
+ int k = get_key(i, dbnum, which);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_pri_seq(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ const int dbnum = 0;
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ int expectk = get_key(i, dbnum, 0);
+
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == expectk);
+
+ int num_keys = get_total_num_keys(i, ndbs);
+ assert(val.size == num_keys*sizeof(int));
+ int v[num_keys]; get_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ }
+ assert(i == nrows); // if (i != nrows) printf("%s:%d %d %d\n", __FUNCTION__, __LINE__, i, nrows); // assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_sec_seq(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ assert(dbnum > 0);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ int rows_found = 0;
+
+ for (i = 0; ; i++) {
+ int num_keys = get_num_keys(i, dbnum);
+ for (int which = 0; which < num_keys; ++which) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0) {
+ CKERR2(r, DB_NOTFOUND);
+ goto done;
+ }
+ rows_found++;
+ int k;
+ int expectk = get_key(i, dbnum, which);
+
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ int got_i = (ntohl(k) >> 16) / 2;
+ if (got_i < i) {
+ // Will fail. Too many old i's
+ assert(k == expectk);
+ } else if (got_i > i) {
+ // Will fail. Too few in previous i.
+ assert(k == expectk);
+ }
+
+ if (k != expectk && which < get_num_new_keys(i, dbnum) && k == get_new_key(i, dbnum, which)) {
+ // Will fail, never got updated.
+ assert(k == expectk);
+ }
+ assert(k == expectk);
+ assert(val.size == 0);
+ }
+ }
+done:
+ assert(rows_found == get_total_secondary_rows(nrows));
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0);
+ assert_zero(r);
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ // update multiple key0
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ update_diagonal(env, txn, db, ndbs, nrows);
+
+ toku_hard_crash_on_purpose();
+}
+
+static void
+verify_all(DB_ENV *env, int ndbs, int nrows) {
+ int r;
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ if (dbnum == 0) {
+ verify_pri_seq(env, db, ndbs, nrows);
+ } else {
+ verify_sec_seq(env, db, dbnum, nrows);
+ }
+ r = db->close(db, 0);
+ assert_zero(r);
+ }
+}
+
+static void
+run_recover(int ndbs, int nrows) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ verify_all(env, ndbs, nrows);
+ r = env->close(env, 0); assert_zero(r);
+}
+
+static int
+usage(void) {
+ return 1;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ int ndbs = 2;
+ int nrows = 3*(1<<5)*4;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+ while (nrows % (3*(1<<5)) != 0) {
+ nrows++;
+ }
+
+ if (do_test)
+ run_test(ndbs, nrows);
+ if (do_recover)
+ run_recover(ndbs, nrows);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update-multiple.cc b/storage/tokudb/PerconaFT/src/tests/recover-update-multiple.cc
new file mode 100644
index 00000000000..f2f5d09fa5c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update-multiple.cc
@@ -0,0 +1,507 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of some update multiple operations
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static int
+get_num_new_keys(int i, int dbnum) {
+ if (dbnum == 0) return 1;
+ if (i & (1<<4)) {
+ dbnum++; // Shift every once in a while.
+ }
+ return (i + dbnum) % 3; // 0, 1, or 2
+}
+
+static int
+get_num_keys(int i, int dbnum) {
+ if (dbnum == 0) return 1;
+ return (i + dbnum) % 3; // 0, 1, or 2
+}
+
+static int
+get_total_secondary_rows(int num_primary) {
+ assert(num_primary % 3 == 0);
+ return num_primary / 3 * (0 + 1 + 2);
+}
+
+static int
+get_total_num_keys(int i, int num_dbs) {
+ int sum = 0;
+ for (int db = 1; db < num_dbs; ++db) {
+ sum += get_num_keys(i, db);
+ }
+ return sum;
+}
+
+static int
+get_total_num_new_keys(int i, int num_dbs) {
+ int sum = 0;
+ for (int db = 1; db < num_dbs; ++db) {
+ sum += get_num_new_keys(i, db);
+ }
+ return sum;
+}
+
+static int
+get_key(int i, int dbnum, int which) {
+ assert(i < INT16_MAX / 2);
+ assert(which >= 0);
+ assert(which < get_num_keys(i, dbnum));
+ assert(which < 4);
+ assert(dbnum < 16);
+ if (dbnum == 0) {
+ assert(which == 0);
+ return htonl((2*i) << 16);
+ } else {
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1));
+ }
+}
+
+static int
+get_new_key(int i, int dbnum, int which) {
+ assert(which >= 0);
+ assert(which < get_num_new_keys(i, dbnum));
+ assert(which < 4);
+ assert(dbnum < 16);
+
+ if (dbnum == 0) {
+ assert(which == 0);
+ return htonl((2*i+1) << 16);
+ } else if ((i+dbnum+which) & (1<<5)) {
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1)); // no change from original
+ } else {
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1) + 1);
+ }
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ int index = 0;
+ for (int dbnum = 1; dbnum < ndbs; dbnum++) {
+ for (int which = 0; which < get_num_keys(i, dbnum); ++which) {
+ v[index++] = get_key(i, dbnum, which);
+ }
+ }
+}
+
+static void
+get_new_data(int *v, int i, int ndbs) {
+ int index = 0;
+ for (int dbnum = 1; dbnum < ndbs; dbnum++) {
+ for (int which = 0; which < get_num_new_keys(i, dbnum); ++which) {
+ v[index++] = get_new_key(i, dbnum, which);
+ if (which > 0) {
+ assert(index >= 2);
+ assert(memcmp(&v[index-2], &v[index-1], sizeof(v[0])) < 0);
+ }
+ }
+ }
+}
+
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ (void)src_val;
+ assert(src_db != dest_db);
+ assert(src_db);
+ int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum > 0);
+
+ int pri_key = *(int *) src_key->data;
+ int* pri_val = (int*) src_val->data;
+
+ bool is_new = (ntohl(pri_key) >> 16) % 2 == 1;
+ int i = (ntohl(pri_key) >> 16) / 2;
+
+ int num_keys = is_new ? get_num_new_keys(i, dbnum) : get_num_keys(i, dbnum);
+
+ toku_dbt_array_resize(dest_key_arrays, num_keys);
+
+ if (dest_val_arrays) {
+ toku_dbt_array_resize(dest_val_arrays, num_keys);
+ }
+
+ int index = 0;
+
+ for (int idb = 1; idb < dbnum; idb++) {
+ index += is_new ? get_num_new_keys(i, idb) : get_num_keys(i, idb);
+ }
+ assert(src_val->size % sizeof(int) == 0);
+ assert((int)src_val->size / 4 >= index + num_keys);
+
+ for (int which = 0; which < num_keys; which++) {
+ DBT *dest_key = &dest_key_arrays->dbts[which];
+ DBT *dest_val = NULL;
+
+ assert(dest_key->flags == DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(int));
+ dest_key->ulen = sizeof(int);
+ }
+ dest_key->size = sizeof(int);
+ if (dest_val_arrays) {
+ dest_val = &dest_val_arrays->dbts[which];
+ assert(dest_val->flags == DB_DBT_REALLOC);
+ dest_val->size = 0;
+ }
+ int new_key = is_new ? get_new_key(i, dbnum, which) : get_key(i, dbnum, which);
+ assert(new_key == pri_val[index + which]);
+ *(int*)dest_key->data = new_key;
+ }
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_key_arrays, NULL, src_key, src_data);
+}
+
+static void
+update_diagonal(DB_ENV *env, DB_TXN *txn, DB *db[], int ndbs, int nrows) {
+ assert(ndbs > 0);
+ int r;
+
+ int narrays = 2 * ndbs;
+ DBT_ARRAY keys[narrays];
+ DBT_ARRAY vals[narrays];
+ for (int i = 0; i < narrays; i++) {
+ toku_dbt_array_init(&keys[i], 1);
+ toku_dbt_array_init(&vals[i], 1);
+ }
+
+ for (int i = 0; i < nrows; i++) {
+
+ // update the data i % ndbs col from x to x+1
+
+ int old_k = get_key(i, 0, 0);
+ DBT old_key; dbt_init(&old_key, &old_k, sizeof old_k);
+ int new_k = get_new_key(i, 0, 0);
+ DBT new_key; dbt_init(&new_key, &new_k, sizeof new_k);
+
+ int num_old_keys = get_total_num_keys(i, ndbs);
+ int v[num_old_keys]; get_data(v, i, ndbs);
+ DBT old_data; dbt_init(&old_data, &v[0], sizeof v);
+
+ int num_new_keys = get_total_num_new_keys(i, ndbs);
+ int newv[num_new_keys]; get_new_data(newv, i, ndbs);
+ DBT new_data; dbt_init(&new_data, &newv[0], sizeof newv);
+
+ uint32_t flags_array[ndbs]; memset(flags_array, 0, sizeof(flags_array));
+
+ r = env->update_multiple(env, db[0], txn, &old_key, &old_data, &new_key, &new_data, ndbs, db, flags_array, narrays, keys, narrays, vals);
+ assert_zero(r);
+ }
+ for (int i = 0; i < narrays; i++) {
+ toku_dbt_array_destroy(&keys[i]);
+ toku_dbt_array_destroy(&vals[i]);
+ }
+
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0, 0);
+ int secondary_keys = get_total_num_keys(i, ndbs);
+ int v[secondary_keys]; get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ for (int which = 0; which < get_num_keys(i, dbnum); which++) {
+ int k = get_key(i, dbnum, which);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_pri_seq(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ const int dbnum = 0;
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ int expectk = get_new_key(i, dbnum, 0);
+
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == expectk);
+
+ int num_keys = get_total_num_new_keys(i, ndbs);
+ assert(val.size == num_keys*sizeof(int));
+ int v[num_keys]; get_new_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ }
+ assert(i == nrows); // if (i != nrows) printf("%s:%d %d %d\n", __FUNCTION__, __LINE__, i, nrows); // assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_sec_seq(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ assert(dbnum > 0);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ int rows_found = 0;
+
+ for (i = 0; ; i++) {
+ int num_keys = get_num_new_keys(i, dbnum);
+ for (int which = 0; which < num_keys; ++which) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0) {
+ CKERR2(r, DB_NOTFOUND);
+ goto done;
+ }
+ rows_found++;
+ int k;
+ int expectk = get_new_key(i, dbnum, which);
+
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ int got_i = (ntohl(k) >> 16) / 2;
+ if (got_i < i) {
+ // Will fail. Too many old i's
+ assert(k == expectk);
+ } else if (got_i > i) {
+ // Will fail. Too few in previous i.
+ assert(k == expectk);
+ }
+
+ if (k != expectk && which < get_num_keys(i, dbnum) && k == get_key(i, dbnum, which)) {
+ // Will fail, never got updated.
+ assert(k == expectk);
+ }
+ assert(k == expectk);
+ assert(val.size == 0);
+ }
+ }
+done:
+ assert(rows_found == get_total_secondary_rows(nrows));
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0);
+ assert_zero(r);
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert_zero(r);
+
+ // update multiple key0
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ update_diagonal(env, txn, db, ndbs, nrows);
+
+ r = txn->commit(txn, 0); assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0) {
+ verify_pri_seq(env, db[0], ndbs, nrows);
+ } else {
+ verify_sec_seq(env, db[dbnum], dbnum, nrows);
+ }
+ }
+
+ toku_hard_crash_on_purpose();
+}
+
+
+static void
+verify_all(DB_ENV *env, int ndbs, int nrows) {
+ int r;
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert_zero(r);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db->open(db, NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666);
+ assert_zero(r);
+ if (dbnum == 0) {
+ verify_pri_seq(env, db, ndbs, nrows);
+ } else {
+ verify_sec_seq(env, db, dbnum, nrows);
+ }
+ r = db->close(db, 0);
+ assert_zero(r);
+ }
+}
+
+static void
+run_recover(int ndbs, int nrows) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert_zero(r);
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ verify_all(env, ndbs, nrows);
+ r = env->close(env, 0); assert_zero(r);
+}
+
+static int
+usage(void) {
+ return 1;
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ bool do_test = false;
+ bool do_recover = false;
+ int ndbs = 2;
+ int nrows = 3*(1<<5)*4;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+ while (nrows % (3*(1<<5)) != 0) {
+ nrows++;
+ }
+
+ if (do_test)
+ run_test(ndbs, nrows);
+ if (do_recover)
+ run_recover(ndbs, nrows);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_aborts.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_aborts.cc
new file mode 100644
index 00000000000..8b438891d69
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_aborts.cc
@@ -0,0 +1,215 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ if (should_update(i)) {
+ e = _e(i);
+ r = db->update(db, txn, keyp, extrap, 0);
+ CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_unchanged(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _v(i));
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_unchanged(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_aborts_before_checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_aborts_before_checkpoint.cc
new file mode 100644
index 00000000000..47de83775e4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_aborts_before_checkpoint.cc
@@ -0,0 +1,215 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ if (should_update(i)) {
+ e = _e(i);
+ r = db->update(db, txn, keyp, extrap, 0);
+ CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_unchanged(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _v(i));
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_unchanged(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_aborts_before_close.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_aborts_before_close.cc
new file mode 100644
index 00000000000..34ddfcd27dc
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_aborts_before_close.cc
@@ -0,0 +1,215 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ if (should_update(i)) {
+ e = _e(i);
+ r = db->update(db, txn, keyp, extrap, 0);
+ CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_unchanged(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _v(i));
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_unchanged(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts.cc
new file mode 100644
index 00000000000..01ab177bb15
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts.cc
@@ -0,0 +1,206 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0);
+ CKERR(r);
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_unchanged(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _v(i));
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_unchanged(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts2.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts2.cc
new file mode 100644
index 00000000000..2afdf2ce538
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts2.cc
@@ -0,0 +1,208 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0);
+ CKERR(r);
+ return r;
+}
+
+DB_ENV *env;
+DB *db;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+}
+
+static void run_test(void)
+{
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback2(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_unchanged(void)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _v(i));
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_unchanged(); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts3.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts3.cc
new file mode 100644
index 00000000000..32a4e03c698
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts3.cc
@@ -0,0 +1,208 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0);
+ CKERR(r);
+ return r;
+}
+
+DB_ENV *env;
+DB *db;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+}
+
+static void run_test(void)
+{
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_unchanged(void)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _v(i));
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_unchanged(); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts_before_checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts_before_checkpoint.cc
new file mode 100644
index 00000000000..f261b59dd66
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts_before_checkpoint.cc
@@ -0,0 +1,206 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0);
+ CKERR(r);
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_unchanged(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _v(i));
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_unchanged(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts_before_close.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts_before_close.cc
new file mode 100644
index 00000000000..f2d26c7771c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_aborts_before_close.cc
@@ -0,0 +1,206 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0);
+ CKERR(r);
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_unchanged(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _v(i));
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_unchanged(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values.cc
new file mode 100644
index 00000000000..90c80a9e18b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values.cc
@@ -0,0 +1,210 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0);
+ CKERR(r);
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_updated(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_updated(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values2.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values2.cc
new file mode 100644
index 00000000000..24c8891a422
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values2.cc
@@ -0,0 +1,213 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0);
+ CKERR(r);
+ return r;
+}
+
+DB_ENV *env;
+DB *db;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+}
+
+
+static void run_test(void)
+{
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback2(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_updated(void)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0);
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_updated(); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values3.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values3.cc
new file mode 100644
index 00000000000..8a14c845c2b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values3.cc
@@ -0,0 +1,211 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0); CKERR(r);
+ return r;
+}
+
+DB_ENV *env;
+DB *db;
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+}
+
+
+static void run_test(void)
+{
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_updated(void)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_updated(); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values_before_checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values_before_checkpoint.cc
new file mode 100644
index 00000000000..8705096daf0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values_before_checkpoint.cc
@@ -0,0 +1,207 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0); CKERR(r);
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_updated(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_updated(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values_before_close.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values_before_close.cc
new file mode 100644
index 00000000000..b38f2d61c66
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_broadcast_changes_values_before_close.cc
@@ -0,0 +1,207 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ v = _u(*ov, _e(*k));
+
+ if (should_update(*k)) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, 0); CKERR(r);
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_updated(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_updated(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values.cc
new file mode 100644
index 00000000000..8faf0084dac
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values.cc
@@ -0,0 +1,216 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ if (should_update(i)) {
+ e = _e(i);
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_updated(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_updated(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values_before_checkpoint.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values_before_checkpoint.cc
new file mode 100644
index 00000000000..c94a47b0abd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values_before_checkpoint.cc
@@ -0,0 +1,216 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ if (should_update(i)) {
+ e = _e(i);
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_updated(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_updated(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values_before_close.cc b/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values_before_close.cc
new file mode 100644
index 00000000000..09f75ef7a0a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-update_changes_values_before_close.cc
@@ -0,0 +1,216 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify recovery of an update log entry which changes values at keys
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+static const unsigned int NUM_KEYS = 100;
+
+static inline bool should_update(const unsigned int k) { return k % 3 == 0; }
+
+static inline unsigned int _v(const unsigned int k) { return 10 - k; }
+static inline unsigned int _e(const unsigned int k) { return k + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int do_inserts(DB_TXN *txn, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ if (should_update(i)) {
+ e = _e(i);
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void run_test(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ toku_hard_crash_on_purpose();
+}
+
+static int verify_updated(DB_ENV *env, DB *db)
+{
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn_1, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ });
+
+ return r;
+}
+
+static void run_recover(void)
+{
+ DB_ENV *env;
+ DB *db;
+
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ { int chk_r = verify_updated(env, db); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int usage(void)
+{
+ return 1;
+}
+
+int test_main(int argc, char * const argv[])
+{
+ bool do_test = false;
+ bool do_recover = false;
+
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose--;
+ if (verbose < 0)
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--test") == 0) {
+ do_test = true;
+ continue;
+ }
+ if (strcmp(arg, "--recover") == 0) {
+ do_recover = true;
+ continue;
+ }
+ if (strcmp(arg, "--help") == 0) {
+ return usage();
+ }
+ }
+
+ if (do_test) {
+ run_test();
+ }
+ if (do_recover) {
+ run_recover();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-upgrade-db-descriptor-multihandle.cc b/storage/tokudb/PerconaFT/src/tests/recover-upgrade-db-descriptor-multihandle.cc
new file mode 100644
index 00000000000..8423707e904
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-upgrade-db-descriptor-multihandle.cc
@@ -0,0 +1,327 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the comparison function get a valid db object pointer
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const char *descriptor_contents[] = {
+ "Spoon full of sugar",
+ "Bucket full of pants"
+};
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+
+int verified = 0;
+uint32_t forced_version = 2;
+
+static int my_compare(DB *UU(db), const DBT *a, const DBT *b) {
+ assert(db);
+ assert(db->cmp_descriptor);
+ uint32_t which = forced_version-1;
+ size_t len = strlen(descriptor_contents[which])+1;
+
+ assert(db->cmp_descriptor->dbt.size == len);
+ assert(memcmp(db->cmp_descriptor->dbt.data, descriptor_contents[which], len) == 0);
+
+ assert(a->size == b->size);
+ verified = 1;
+ return memcmp(a->data, b->data, a->size);
+}
+
+static void
+change_descriptor(DB* db, int which, DB_ENV* env) {
+ DBT descriptor;
+ size_t len = strlen(descriptor_contents[which])+1;
+ dbt_init(&descriptor, descriptor_contents[which], len);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db->change_descriptor(db, txn_desc, &descriptor, DB_UPDATE_CMP_DESCRIPTOR); CKERR(chk_r); }
+ });
+}
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char datadir[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(datadir, 2, TOKU_TEST_FILENAME, "data"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_data_dir(env, "data"); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ change_descriptor(dba, 0, env);
+
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ change_descriptor(dbb, 1, env);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = dba->put(dba, txn, &b, &a, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ assert(verified);
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba;
+ int r;
+ char datadir[TOKU_PATH_MAX+1];
+ toku_path_join(datadir, 2, TOKU_TEST_FILENAME, "data");
+ toku_os_recursive_delete(datadir);
+ r = toku_os_mkdir(datadir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_data_dir(env, "data"); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == 0);
+ assert(aa.size == 2 && ab.size == 2 && memcmp(aa.data, b, 2) == 0 && memcmp(ab.data, a, 2) == 0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ assert(verified);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit)
+{
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ } else if (do_no_recover) {
+ do_x1_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-upgrade-db-descriptor.cc b/storage/tokudb/PerconaFT/src/tests/recover-upgrade-db-descriptor.cc
new file mode 100644
index 00000000000..e195f95af0e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-upgrade-db-descriptor.cc
@@ -0,0 +1,330 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the comparison function get a valid db object pointer
+
+#include <sys/stat.h>
+#include "test.h"
+
+
+const char *descriptor_contents[] = {
+ "Spoon full of sugar",
+ "Bucket full of pants"
+};
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+
+int verified = 0;
+uint32_t forced_version = 2;
+
+static int my_compare(DB *UU(db), const DBT *a, const DBT *b) {
+ assert(db);
+ assert(db->cmp_descriptor);
+ uint32_t which = forced_version-1;
+ size_t len = strlen(descriptor_contents[which])+1;
+
+ assert(db->cmp_descriptor->dbt.size == len);
+ assert(memcmp(db->cmp_descriptor->dbt.data, descriptor_contents[which], len) == 0);
+
+ assert(a->size == b->size);
+ verified = 1;
+ return memcmp(a->data, b->data, a->size);
+}
+
+static void
+change_descriptor(DB* db, int which, DB_ENV* env) {
+ DBT descriptor;
+ size_t len = strlen(descriptor_contents[which])+1;
+ dbt_init(&descriptor, descriptor_contents[which], len);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db->change_descriptor(db, txn_desc, &descriptor, DB_UPDATE_CMP_DESCRIPTOR); CKERR(chk_r); }
+ });
+}
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ char datadir[TOKU_PATH_MAX+1];
+ r = toku_os_mkdir(toku_path_join(datadir, 2, TOKU_TEST_FILENAME, "data"), S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ DB_ENV *env;
+ DB *dba;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_data_dir(env, "data"); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ change_descriptor(dba, 0, env);
+ r = dba->close(dba, 0); CKERR(r);
+
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ change_descriptor(dba, 1, env);
+
+
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = dba->put(dba, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ //printf("shutdown\n");
+ assert(verified);
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba;
+ int r;
+ char datadir[TOKU_PATH_MAX+1];
+ toku_path_join(datadir, 2, TOKU_TEST_FILENAME, "data");
+ toku_os_recursive_delete(datadir);
+ r = toku_os_mkdir(datadir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_data_dir(env, "data"); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == 0);
+ assert(aa.size == 2 && ab.size == 2 && memcmp(aa.data, b, 2) == 0 && memcmp(ab.data, a, 2) == 0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ assert(verified);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit)
+{
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ } else if (do_no_recover) {
+ do_x1_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-x1-abort.cc b/storage/tokudb/PerconaFT/src/tests/recover-x1-abort.cc
new file mode 100644
index 00000000000..7a4db46a2fc
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-x1-abort.cc
@@ -0,0 +1,304 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Transaction consistency:
+ * fork a process:
+ * Open two tables, T1 and T2
+ * begin transaction
+ * store A in T1
+ * checkpoint
+ * store B in T2
+ * commit (or abort)
+ * signal to end the process abruptly
+ * wait for the process to finish
+ * open the environment doing recovery
+ * check to see if both A and B are present (or absent)
+ */
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba, *dbb;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DBT ba, bb;
+ dbt_init(&ba, NULL, 0);
+ dbt_init(&bb, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca,*cb;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ r = dbb->cursor(dbb, txn, &cb, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ int rb = cb->c_get(cb, &ba, &bb, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ assert(rb==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ assert(ba.size==2);
+ assert(bb.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(bb.data, &a, 2)==0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ assert(rb==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = cb->c_close(cb); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+static void
+do_test_internal (bool commit)
+{
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+#endif
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0 || strcmp(argv[0], "--test") == 0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char *const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ } else if (do_no_recover) {
+ do_x1_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-x1-commit.cc b/storage/tokudb/PerconaFT/src/tests/recover-x1-commit.cc
new file mode 100644
index 00000000000..237182b457e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-x1-commit.cc
@@ -0,0 +1,307 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Transaction consistency:
+ * fork a process:
+ * Open two tables, T1 and T2
+ * begin transaction
+ * store A in T1
+ * checkpoint
+ * store B in T2
+ * commit (or abort)
+ * signal to end the process abruptly
+ * wait for the process to finish
+ * open the environment doing recovery
+ * check to see if both A and B are present (or absent)
+ */
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ if (do_commit) {
+ r = txn->commit(txn, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba, *dbb;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DBT ba, bb;
+ dbt_init(&ba, NULL, 0);
+ dbt_init(&bb, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca,*cb;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ r = dbb->cursor(dbb, txn, &cb, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ int rb = cb->c_get(cb, &ba, &bb, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ assert(rb==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ assert(ba.size==2);
+ assert(bb.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(bb.data, &a, 2)==0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ assert(rb==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = cb->c_close(cb); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == DB_RUNRECOVERY);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit)
+{
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false, do_no_recover = false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (do_no_recover) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[])
+{
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ } else if (do_no_recover) {
+ do_x1_no_recover();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-x1-nested-abort.cc b/storage/tokudb/PerconaFT/src/tests/recover-x1-nested-abort.cc
new file mode 100644
index 00000000000..8262740563b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-x1-nested-abort.cc
@@ -0,0 +1,290 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Transaction consistency:
+ * fork a process:
+ * Open two tables, T1 and T2
+ * begin transaction
+ * store A in T1
+ * checkpoint
+ * store B in T2
+ * commit (or abort)
+ * signal to end the process abruptly
+ * wait for the process to finish
+ * open the environment doing recovery
+ * check to see if both A and B are present (or absent)
+ */
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn0;
+ r = env->txn_begin(env, NULL, &txn0, 0); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, txn0, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ r = txn->commit(txn, 0); CKERR(r);
+ if (do_commit) {
+ r = txn->commit(txn0, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn0); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba, *dbb;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DBT ba, bb;
+ dbt_init(&ba, NULL, 0);
+ dbt_init(&bb, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca,*cb;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ r = dbb->cursor(dbb, txn, &cb, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ int rb = cb->c_get(cb, &ba, &bb, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ assert(rb==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ assert(ba.size==2);
+ assert(bb.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(bb.data, &a, 2)==0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ assert(rb==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = cb->c_close(cb); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit) {
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0 || strcmp(argv[0], "--test") == 0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-x1-nested-commit.cc b/storage/tokudb/PerconaFT/src/tests/recover-x1-nested-commit.cc
new file mode 100644
index 00000000000..c2a01db83b7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-x1-nested-commit.cc
@@ -0,0 +1,291 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Transaction consistency:
+ * fork a process:
+ * Open two tables, T1 and T2
+ * begin transaction
+ * store A in T1
+ * checkpoint
+ * store B in T2
+ * commit (or abort)
+ * signal to end the process abruptly
+ * wait for the process to finish
+ * open the environment doing recovery
+ * check to see if both A and B are present (or absent)
+ */
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+
+static void
+do_x1_shutdown (bool do_commit, bool do_abort) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba, *dbb;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txn0;
+ r = env->txn_begin(env, NULL, &txn0, 0); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, txn0, &txn, 0); CKERR(r);
+ {
+ DBT a,b;
+ dbt_init(&a, "a", 2);
+ dbt_init(&b, "b", 2);
+ r = dba->put(dba, txn, &a, &b, 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ r = dbb->put(dbb, txn, &b, &a, 0); CKERR(r);
+ }
+ //printf("opened\n");
+ r = txn->commit(txn, 0); CKERR(r);
+ txn = NULL;
+ if (do_commit) {
+ r = txn0->commit(txn0, 0); CKERR(r);
+ } else if (do_abort) {
+ r = txn->abort(txn0); CKERR(r);
+
+ // force an fsync of the log
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = txn->commit(txn, DB_TXN_SYNC); CKERR(r);
+ }
+ //printf("shutdown\n");
+ toku_hard_crash_on_purpose();
+}
+
+static void
+do_x1_recover (bool did_commit) {
+ DB_ENV *env;
+ DB *dba, *dbb;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBT aa, ab;
+ dbt_init(&aa, NULL, 0);
+ dbt_init(&ab, NULL, 0);
+ DBT ba, bb;
+ dbt_init(&ba, NULL, 0);
+ dbt_init(&bb, NULL, 0);
+ DB_TXN *txn;
+ DBC *ca,*cb;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = dba->cursor(dba, txn, &ca, 0); CKERR(r);
+ r = dbb->cursor(dbb, txn, &cb, 0); CKERR(r);
+ int ra = ca->c_get(ca, &aa, &ab, DB_FIRST); CKERR(r);
+ int rb = cb->c_get(cb, &ba, &bb, DB_FIRST); CKERR(r);
+ if (did_commit) {
+ assert(ra==0);
+ assert(rb==0);
+ // verify key-value pairs
+ assert(aa.size==2);
+ assert(ab.size==2);
+ assert(ba.size==2);
+ assert(bb.size==2);
+ const char a[2] = "a";
+ const char b[2] = "b";
+ assert(memcmp(aa.data, &a, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(ab.data, &b, 2)==0);
+ assert(memcmp(bb.data, &a, 2)==0);
+ // make sure no other entries in DB
+ assert(ca->c_get(ca, &aa, &ab, DB_NEXT) == DB_NOTFOUND);
+ assert(cb->c_get(cb, &ba, &bb, DB_NEXT) == DB_NOTFOUND);
+ fprintf(stderr, "Both verified. Yay!\n");
+ } else {
+ // It wasn't committed (it also wasn't aborted), but a checkpoint happened.
+ assert(ra==DB_NOTFOUND);
+ assert(rb==DB_NOTFOUND);
+ fprintf(stderr, "Neither present. Yay!\n");
+ }
+ r = ca->c_close(ca); CKERR(r);
+ r = cb->c_close(cb); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void
+do_x1_recover_only (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit) {
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+bool do_commit=false, do_abort=false, do_explicit_abort=false, do_recover_committed=false, do_recover_aborted=false, do_recover_only=false;
+
+static void
+x1_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0], "--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0], "--explicit-abort")==0) {
+ do_explicit_abort=true;
+ } else if (strcmp(argv[0], "--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0], "--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--commit | --abort | --explicit-abort | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_explicit_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (do_recover_only) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ x1_parse_args(argc, argv);
+ if (do_commit) {
+ do_x1_shutdown (true, false);
+ } else if (do_abort) {
+ do_x1_shutdown (false, false);
+ } else if (do_explicit_abort) {
+ do_x1_shutdown(false, true);
+ } else if (do_recover_committed) {
+ do_x1_recover(true);
+ } else if (do_recover_aborted) {
+ do_x1_recover(false);
+ } else if (do_recover_only) {
+ do_x1_recover_only();
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-x2-abort.cc b/storage/tokudb/PerconaFT/src/tests/recover-x2-abort.cc
new file mode 100644
index 00000000000..51aa281434e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-x2-abort.cc
@@ -0,0 +1,267 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Transaction consistency:
+ * fork a process:
+ * Open two tables, A and B
+ * begin transaction U
+ * begin transaction V
+ * store U.A into A using U
+ * store V.B into B using V
+ * checkpoint
+ * store U.C into A using U
+ * store V.D into B using V
+ * commit U
+ * maybe commit V
+ * abort the process abruptly
+ * wait for the process to finish
+ * open the environment doing recovery
+ * check to see if both rows are present in A and maybe present in B
+ */
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void
+put (DB_TXN *txn, DB *db, const char *key, const char *data) {
+ DBT k,d;
+ dbt_init(&k, key, 1+strlen(key));
+ dbt_init(&d, data, 1+strlen(data));
+ int r = db->put(db, txn, &k, &d, 0);
+ CKERR(r);
+}
+
+static void
+do_x2_shutdown (bool do_commit) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba, *dbb; // Use two DBs so that BDB doesn't get a lock conflict
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txnU, *txnV;
+ r = env->txn_begin(env, NULL, &txnU, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnV, 0); CKERR(r);
+ put(txnU, dba, "u.a", "u.a.data");
+ put(txnV, dbb, "v.b", "v.b.data");
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ put(txnU, dba, "u.c", "u.c.data");
+ put(txnV, dbb, "v.d", "v.d.data");
+ r = txnU->commit(txnU, 0); CKERR(r);
+ if (do_commit) {
+ r = txnV->commit(txnV, 0); CKERR(r);
+ }
+ toku_hard_crash_on_purpose();
+}
+
+static void
+checkcurs (DBC *curs, int cursflags, const char *key, const char *val, bool expect_it) {
+ DBT k,v;
+ dbt_init(&k, NULL, 0);
+ dbt_init(&v, NULL, 0);
+ int r = curs->c_get(curs, &k, &v, cursflags);
+ if (expect_it) {
+ assert(r==0);
+ printf("Got %s expected %s\n", (char*)k.data, key);
+ assert(strcmp((char*)k.data, key)==0);
+ assert(strcmp((char*)v.data, val)==0);
+ } else {
+ printf("Expected nothing, got r=%d\n", r);
+ assert(r!=0);
+ }
+}
+
+static void
+do_x2_recover (bool did_commit) {
+ DB_ENV *env;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBC *c;
+ r = dba->cursor(dba, txn, &c, 0); CKERR(r);
+ checkcurs(c, DB_FIRST, "u.a", "u.a.data", true);
+ checkcurs(c, DB_NEXT, "u.c", "u.c.data", true);
+ checkcurs(c, DB_NEXT, NULL, NULL, false);
+ r = c->c_close(c); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ }
+ {
+ DB *dbb;
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBC *c;
+ r = dbb->cursor(dbb, txn, &c, 0); CKERR(r);
+ checkcurs(c, DB_FIRST, "v.b", "v.b.data", did_commit);
+ checkcurs(c, DB_NEXT, "v.d", "v.d.data", did_commit);
+ checkcurs(c, DB_NEXT, NULL, NULL, false);
+ r = c->c_close(c); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ }
+
+ r = txn->commit(txn, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit) {
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+bool do_commit=false, do_abort=false, do_recover_committed=false, do_recover_aborted=false;
+
+static void
+x2_parse_args (int argc, char *const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0],"--abort")==0 || strcmp(argv[0], "--test") == 0) {
+ do_abort=true;
+ } else if (strcmp(argv[0],"--commit")==0) {
+ do_commit=true;
+ } else if (strcmp(argv[0],"--recover-committed")==0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0],"--recover-aborted")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--abort | --commit | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ x2_parse_args(argc, argv);
+ if (do_commit) {
+ do_x2_shutdown (true);
+ } else if (do_abort) {
+ do_x2_shutdown (false);
+ } else if (do_recover_committed) {
+ do_x2_recover(true);
+ } else if (do_recover_aborted) {
+ do_x2_recover(false);
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recover-x2-commit.cc b/storage/tokudb/PerconaFT/src/tests/recover-x2-commit.cc
new file mode 100644
index 00000000000..fdff0f1d52c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recover-x2-commit.cc
@@ -0,0 +1,267 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Transaction consistency:
+ * fork a process:
+ * Open two tables, A and B
+ * begin transaction U
+ * begin transaction V
+ * store U.A into A using U
+ * store V.B into B using V
+ * checkpoint
+ * store U.C into A using U
+ * store V.D into B using V
+ * commit U
+ * maybe commit V
+ * abort the process abruptly
+ * wait for the process to finish
+ * open the environment doing recovery
+ * check to see if both rows are present in A and maybe present in B
+ */
+#include <sys/stat.h>
+#include "test.h"
+
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+const char *namea="a.db";
+const char *nameb="b.db";
+
+static void
+put (DB_TXN *txn, DB *db, const char *key, const char *data) {
+ DBT k,d;
+ dbt_init(&k, key, 1+strlen(key));
+ dbt_init(&d, data, 1+strlen(data));
+ int r = db->put(db, txn, &k, &d, 0);
+ CKERR(r);
+}
+
+static void
+do_x2_shutdown (bool do_commit) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ DB *dba, *dbb; // Use two DBs so that BDB doesn't get a lock conflict
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dba->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DB_TXN *txnU, *txnV;
+ r = env->txn_begin(env, NULL, &txnU, 0); CKERR(r);
+ r = env->txn_begin(env, NULL, &txnV, 0); CKERR(r);
+ put(txnU, dba, "u.a", "u.a.data");
+ put(txnV, dbb, "v.b", "v.b.data");
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ put(txnU, dba, "u.c", "u.c.data");
+ put(txnV, dbb, "v.d", "v.d.data");
+ r = txnU->commit(txnU, 0); CKERR(r);
+ if (do_commit) {
+ r = txnV->commit(txnV, 0); CKERR(r);
+ }
+ toku_hard_crash_on_purpose();
+}
+
+static void
+checkcurs (DBC *curs, int cursflags, const char *key, const char *val, bool expect_it) {
+ DBT k,v;
+ dbt_init(&k, NULL, 0);
+ dbt_init(&v, NULL, 0);
+ int r = curs->c_get(curs, &k, &v, cursflags);
+ if (expect_it) {
+ assert(r==0);
+ printf("Got %s expected %s\n", (char*)k.data, key);
+ assert(strcmp((char*)k.data, key)==0);
+ assert(strcmp((char*)v.data, val)==0);
+ } else {
+ printf("Expected nothing, got r=%d\n", r);
+ assert(r!=0);
+ }
+}
+
+static void
+do_x2_recover (bool did_commit) {
+ DB_ENV *env;
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ {
+ DB *dba;
+ r = db_create(&dba, env, 0); CKERR(r);
+ r = dba->open(dba, NULL, namea, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBC *c;
+ r = dba->cursor(dba, txn, &c, 0); CKERR(r);
+ checkcurs(c, DB_FIRST, "u.a", "u.a.data", true);
+ checkcurs(c, DB_NEXT, "u.c", "u.c.data", true);
+ checkcurs(c, DB_NEXT, NULL, NULL, false);
+ r = c->c_close(c); CKERR(r);
+ r = dba->close(dba, 0); CKERR(r);
+ }
+ {
+ DB *dbb;
+ r = db_create(&dbb, env, 0); CKERR(r);
+ r = dbb->open(dbb, NULL, nameb, NULL, DB_BTREE, DB_AUTO_COMMIT|DB_CREATE, 0666); CKERR(r);
+ DBC *c;
+ r = dbb->cursor(dbb, txn, &c, 0); CKERR(r);
+ checkcurs(c, DB_FIRST, "v.b", "v.b.data", did_commit);
+ checkcurs(c, DB_NEXT, "v.d", "v.d.data", did_commit);
+ checkcurs(c, DB_NEXT, NULL, NULL, false);
+ r = c->c_close(c); CKERR(r);
+ r = dbb->close(dbb, 0); CKERR(r);
+ }
+
+ r = txn->commit(txn, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+#if 0
+
+static void
+do_test_internal (bool commit) {
+ pid_t pid;
+ if (0 == (pid=fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--commit" : "--abort", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("signaled=%d sig=%d\n", WIFSIGNALED(status), WTERMSIG(status));
+ assert(WIFSIGNALED(status) && WTERMSIG(status)==SIGABRT);
+ }
+ // Now find out what happend
+
+ if (0 == (pid = fork())) {
+ int r=execl(cmd, verbose ? "-v" : "-q", commit ? "--recover-committed" : "--recover-aborted", NULL);
+ assert(r==-1);
+ printf("execl failed: %d (%s)\n", errno, strerror(errno));
+ assert(0);
+ }
+ {
+ int r;
+ int status;
+ r = waitpid(pid, &status, 0);
+ //printf("recovery exited=%d\n", WIFEXITED(status));
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+}
+
+static void
+do_test (void) {
+ do_test_internal(true);
+ do_test_internal(false);
+}
+
+#endif
+
+bool do_commit=false, do_abort=false, do_recover_committed=false, do_recover_aborted=false;
+
+static void
+x2_parse_args (int argc, char * const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0],"--abort")==0) {
+ do_abort=true;
+ } else if (strcmp(argv[0],"--commit")==0 || strcmp(argv[0], "--test") == 0) {
+ do_commit=true;
+ } else if (strcmp(argv[0],"--recover-committed")==0 || strcmp(argv[0], "--recover") == 0) {
+ do_recover_committed=true;
+ } else if (strcmp(argv[0],"--recover-aborted")==0) {
+ do_recover_aborted=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--abort | --commit | --recover-committed | --recover-aborted } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ {
+ int n_specified=0;
+ if (do_commit) n_specified++;
+ if (do_abort) n_specified++;
+ if (do_recover_committed) n_specified++;
+ if (do_recover_aborted) n_specified++;
+ if (n_specified>1) {
+ printf("Specify only one of --commit or --abort or --recover-committed or --recover-aborted\n");
+ resultcode=1;
+ goto do_usage;
+ }
+ }
+}
+
+int
+test_main (int argc, char * const argv[]) {
+ x2_parse_args(argc, argv);
+ if (do_commit) {
+ do_x2_shutdown (true);
+ } else if (do_abort) {
+ do_x2_shutdown (false);
+ } else if (do_recover_committed) {
+ do_x2_recover(true);
+ } else if (do_recover_aborted) {
+ do_x2_recover(false);
+ }
+#if 0
+ else {
+ do_test();
+ }
+#endif
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recovery_fileops_stress.cc b/storage/tokudb/PerconaFT/src/tests/recovery_fileops_stress.cc
new file mode 100644
index 00000000000..683952c5d3e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recovery_fileops_stress.cc
@@ -0,0 +1,587 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+static const int NUM_DICTIONARIES = 100;
+//static const int NUM_DICTIONARIES = 3;
+static const char *table = "tbl";
+static const int ROWS_PER_TABLE = 10;
+
+DB_ENV *env;
+DB** db_array;
+DB* states;
+static const int percent_do_op = 20;
+static const int percent_do_abort = 25;
+static const int start_crashing_iter = 10;
+// iterations_per_crash_in_recovery should be an odd number;
+static const int iterations_per_crash_in_recovery = 7;
+const char *state_db_name="states.db";
+
+#define CREATED 0
+#define OPEN 1
+#define CLOSED 2
+#define DELETED 3
+
+#define COMMIT_TXN 0
+#define ABORT_TXN 1
+
+static int commit_or_abort(void) {
+ int i = random() % 100;
+ int rval = ( i < percent_do_abort ) ? ABORT_TXN : COMMIT_TXN;
+ if ( verbose ) {
+ if ( rval == ABORT_TXN ) printf("%s : abort txn\n", __FILE__);
+ }
+ return rval;
+}
+
+static void put_state(int db_num, int state) {
+ int r;
+ DB_TXN* txn;
+ DBT key, val;
+ int key_data = db_num;
+ int val_data = state;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = states->put(states, txn,
+ dbt_init(&key, &key_data, sizeof(key_data)),
+ dbt_init(&val, &val_data, sizeof(val_data)),
+ 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+static int get_state(int db_num) {
+ int r;
+ DBT key, val;
+
+ memset(&val, 0, sizeof(val));
+ r = states->get(states, 0,
+ dbt_init(&key, &db_num, sizeof(db_num)),
+ &val,
+ 0);
+ CKERR(r);
+ int state = *(int*)val.data;
+ return state;
+}
+
+static int crash_timer;
+static void crash_it(void);
+static void crash_it_callback_f(void*);
+static void set_crash_timer(void) {
+ crash_timer = random() % (3 * NUM_DICTIONARIES);
+}
+
+static void update_crash_timer(void) {
+ if ( --crash_timer == 0 ) {
+ // close the states table before we crash
+ int r = states->close(states, 0);
+ CKERR(r);
+ if ( verbose ) {
+ printf("%s : crash\n", __FILE__);
+ fflush(stdout);
+ }
+ crash_it();
+ }
+}
+
+static void env_startup(int recovery_flags);
+static int64_t generate_val(int64_t key);
+static void insert_n(DB *db, DB_TXN *txn, int firstkey, int n);
+static int verify_identical_dbts(const DBT *dbt1, const DBT *dbt2);
+static void verify_sequential_rows(DB* compare_db, int64_t firstkey, int64_t numkeys);
+
+static DB* do_create(char* name, int* next_state) {
+ DB* db = NULL;
+ if ( verbose ) printf("%s : do_create(%s)\n", __FILE__, name);
+ int r;
+ DB_TXN* txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, name, NULL, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ insert_n(db, txn, 0, ROWS_PER_TABLE);
+ if ( commit_or_abort() == COMMIT_TXN ) {
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ *next_state = CREATED;
+ }
+ else {
+ r = db->close(db, 0);
+ db = NULL;
+ CKERR(r);
+ r = txn->abort(txn);
+ CKERR(r);
+ db = NULL;
+ }
+ return db;
+}
+
+static DB* do_open(char* name, int* next_state) {
+ DB* db = NULL;
+ DB_TXN* txn;
+ if ( verbose ) printf("%s : do_open(%s)\n", __FILE__, name);
+ int r;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, name, NULL, DB_UNKNOWN, 0, 0666);
+ CKERR(r);
+ if ( commit_or_abort() == COMMIT_TXN ) {
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ *next_state = OPEN;
+ }
+ else {
+ r = db->close(db, 0);
+ db = NULL;
+ CKERR(r);
+ r = txn->abort(txn);
+ CKERR(r);
+ db = NULL;
+ }
+ return db;
+}
+
+static void do_close(DB* db, char* name, int* next_state) {
+ if ( verbose ) printf("%s : do_close(%s)\n", __FILE__, name);
+ if (!db) printf("db == NULL\n");
+
+ int r = db->close(db, 0);
+ CKERR(r);
+ db = NULL;
+ *next_state = CLOSED;
+}
+
+static void do_delete(char* name, int* next_state) {
+ DB_TXN* txn;
+ if ( verbose ) printf("%s : do_delete(%s)\n", __FILE__, name);
+ int r;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = env->dbremove(env, txn, name, NULL, 0);
+ CKERR(r);
+
+ if ( commit_or_abort() == COMMIT_TXN ) {
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ *next_state = DELETED;
+ }
+ else {
+ r = txn->abort(txn);
+ CKERR(r);
+ }
+}
+
+static int do_random_fileop(int i, int state) {
+ DB* db = db_array[i];
+ int rval = random() % 100;
+// if ( verbose ) printf("%s : %s : DB '%d', state '%d, rval '%d'\n", __FILE__, __FUNCTION__, i, state, rval);
+
+ int next_state = state;
+
+ char fname[100];
+ sprintf(fname, "%s%d.db", table, i);
+
+ if ( rval < percent_do_op ) {
+ switch ( state ) {
+ case CREATED:
+ do_close(db, fname, &next_state);
+ db_array[i] = db = 0;
+ if ( rval < (percent_do_op / 2) ) {
+ do_delete(fname, &next_state);
+ }
+ break;
+ case OPEN:
+ do_close(db, fname, &next_state);
+ db_array[i] = db = 0;
+ if ( rval < (percent_do_op / 2) ) {
+ do_delete(fname, &next_state);
+ }
+ break;
+ case CLOSED:
+ if ( rval < (percent_do_op / 2) ) {
+ db = do_open(fname, &next_state);
+ db_array[i] = db;
+ }
+ else {
+ do_delete(fname, &next_state);
+ }
+ break;
+ case DELETED:
+ db = do_create(fname, &next_state);
+ db_array[i] = db;
+ break;
+ }
+ }
+ return next_state;
+}
+
+static void do_random_fileops(void)
+{
+ int i, state, next_state;
+ DB_TXN *txn;
+ for (i=0;i<NUM_DICTIONARIES;i++) {
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ state = get_state(i);
+ next_state = do_random_fileop(i, state);
+ put_state(i, next_state);
+ { int chk_r = txn->commit(txn, 0); CKERR(chk_r); }
+ update_crash_timer();
+ }
+}
+
+
+static void run_test(int iter){
+ uint32_t recovery_flags = DB_INIT_LOG | DB_INIT_TXN;
+ int r, i;
+
+ XMALLOC_N(NUM_DICTIONARIES, db_array);
+ srand(iter);
+
+ if (iter == 0) {
+ // create working directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ }
+ else
+ recovery_flags += DB_RECOVER;
+
+ // crash somewhat frequently during recovery
+ // first, wait until after first crash
+ if ( iter > start_crashing_iter + 1 ) {
+ // every N cycles, crash in recovery
+ if ( (iter % iterations_per_crash_in_recovery) == 0 ) {
+ // crash at different places in recovery
+ if ( iter & 1 )
+ db_env_set_recover_callback(crash_it_callback_f, NULL);
+ else
+ db_env_set_recover_callback2(crash_it_callback_f, NULL);
+ }
+ }
+
+ env_startup(recovery_flags);
+ if ( verbose ) printf("%s : environment init\n", __FILE__);
+
+ if (iter == 0) {
+ // create a dictionary to store test state
+ r = db_create(&states, env, 0); CKERR(r);
+ r = states->open(states, NULL, state_db_name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ DB_TXN *states_txn;
+ r = env->txn_begin(env, NULL, &states_txn, 0); CKERR(r);
+ for (i=0;i<NUM_DICTIONARIES;i++) {
+ put_state(i, DELETED);
+ }
+ r = states_txn->commit(states_txn, 0); CKERR(r);
+ r = states->close(states, 0); CKERR(r);
+ if ( verbose ) printf("%s : states.db initialized\n", __FILE__);
+ }
+
+ // open the 'states' table
+ r = db_create(&states, env, 0); CKERR(r);
+ r = states->open(states, NULL, state_db_name, NULL, DB_UNKNOWN, 0, 0666); CKERR(r);
+
+ if ( verbose ) printf("%s : === ITERATION %6d ===\n", __FILE__, iter);
+
+ // verify previous results
+ if ( verbose ) printf("%s : verify previous results\n", __FILE__);
+ int state = DELETED;
+ DB* db;
+ char fname[100];
+ if ( iter > 0 ) {
+ for (i=0;i<NUM_DICTIONARIES;i++) {
+ sprintf(fname, "%s%d.db", table, i);
+ state = get_state(i);
+ switch (state) {
+ case CREATED:
+ case OPEN:
+ // open the table
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, fname, NULL, DB_UNKNOWN, 0, 0666); CKERR(r);
+ db_array[i] = db;
+ verify_sequential_rows(db, 0, ROWS_PER_TABLE);
+ // leave table open
+ if (verbose) printf("%s : verified open/created db[%d]\n", __FILE__, i);
+ break;
+ case CLOSED:
+ // open the table
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, fname, NULL, DB_UNKNOWN, 0, 0666); CKERR(r);
+ verify_sequential_rows(db, 0, ROWS_PER_TABLE);
+ // close table
+ r = db->close(db, 0); CKERR(r);
+ db_array[i] = db = NULL;
+ if (verbose) printf("%s : verified closed db[%d]\n", __FILE__, i);
+ break;
+ case DELETED:
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, fname, NULL, DB_UNKNOWN, 0, 0666);
+ if ( r == 0 ) assert(1);
+ db_array[i] = db = NULL;
+ if (verbose) printf("%s : verified db[%d] removed\n", __FILE__, i);
+ break;
+ default:
+ printf("ERROR : Unknown state '%d'\n", state);
+ return;
+ }
+ }
+ }
+ if ( verbose ) printf("%s : previous results verified\n", __FILE__);
+
+ // for each of the dictionaries, perform a fileop some percentage of time (set in do_random_fileop).
+
+ // before checkpoint #1
+ if ( verbose ) printf("%s : before checkpoint #1\n", __FILE__);
+ crash_timer = NUM_DICTIONARIES + 1; // won't go off
+ do_random_fileops();
+
+ // during checkpoint #1
+ if ( verbose ) printf("%s : during checkpoint #1\n", __FILE__);
+ crash_timer = NUM_DICTIONARIES + 1; // won't go off
+
+ if ( iter & 1 )
+ db_env_set_checkpoint_callback((void (*)(void*))do_random_fileops, NULL);
+ else
+ db_env_set_checkpoint_callback2((void (*)(void*))do_random_fileops, NULL);
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ db_env_set_checkpoint_callback(NULL, NULL);
+ db_env_set_checkpoint_callback2(NULL, NULL);
+
+ // randomly fail sometime during the next 3 phases
+ // 1) before the next checkpoint
+ // 2) during the next checkpoint
+ // 3) after the next (final) checkpoint
+
+ if ( iter >= start_crashing_iter ) {
+ set_crash_timer();
+ }
+ else {
+ crash_timer = ( 3 * NUM_DICTIONARIES ) + 1; // won't go off
+ }
+
+ // before checkpoint #2
+ if ( verbose ) printf("%s : before checkpoint #2\n", __FILE__);
+ do_random_fileops();
+
+ // during checkpoint
+ if ( verbose ) printf("%s : during checkpoint #2\n", __FILE__);
+
+ if ( iter & 1 )
+ db_env_set_checkpoint_callback((void (*)(void*))do_random_fileops, NULL);
+ else
+ db_env_set_checkpoint_callback2((void (*)(void*))do_random_fileops, NULL);
+ // checkpoint
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ db_env_set_checkpoint_callback(NULL, NULL);
+ db_env_set_checkpoint_callback2(NULL, NULL);
+
+ // after checkpoint
+ if ( verbose ) printf("%s : after checkpoint #2\n", __FILE__);
+ do_random_fileops();
+
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+
+ for (i=0;i<NUM_DICTIONARIES;i++) {
+ db = db_array[i];
+ state = get_state(i);
+ if ( state == CREATED || state == OPEN ) {
+ r = db->close(db, 0); CKERR(r);
+ db = NULL;
+ }
+ }
+
+ r = states->close(states, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ if ( verbose ) printf("%s : done\n", __FILE__);
+
+ toku_free(db_array);
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+static int iter_arg = 0;
+
+int test_main(int argc, char *const*argv) {
+ do_args(argc, argv);
+ run_test(iter_arg);
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] [-i] \n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-i")==0) {
+ argc--; argv++;
+ iter_arg = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+static void env_startup(int recovery_flags) {
+ int r;
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | recovery_flags;
+ r = db_env_create(&env, 0); CKERR(r);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+ r=env->set_redzone(env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ //Disable auto-checkpointing.
+ r = env->checkpointing_set_period(env, 0); CKERR(r);
+}
+
+static int64_t generate_val(int64_t key) {
+ return key + 314;
+}
+
+static void insert_n(DB *db, DB_TXN *txn, int firstkey, int n) {
+ int64_t k, v;
+ int r, i;
+ DBT key, val;
+
+ if (!db) return;
+
+ for (i = 0; i<n; i++) {
+ k = firstkey + i;
+ v = generate_val(k);
+ dbt_init(&key, &k, sizeof(k));
+ dbt_init(&val, &v, sizeof(v));
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ }
+}
+
+static int verify_identical_dbts(const DBT *dbt1, const DBT *dbt2) {
+ int r = 0;
+ if (dbt1->size != dbt2->size) r = 1;
+ else if (memcmp(dbt1->data, dbt2->data, dbt1->size)!=0) r = 1;
+ return r;
+}
+
+static void verify_sequential_rows(DB* compare_db, int64_t firstkey, int64_t numkeys) {
+ //This does not lock the dbs/grab table locks.
+ //This means that you CANNOT CALL THIS while another thread is modifying the db.
+ //You CAN call it while a txn is open however.
+ int rval = 0;
+ DB_TXN *compare_txn;
+ int r, r1;
+
+ assert(numkeys >= 1);
+ r = env->txn_begin(env, NULL, &compare_txn, DB_READ_UNCOMMITTED);
+ CKERR(r);
+ DBC *c1;
+
+ r = compare_db->cursor(compare_db, compare_txn, &c1, 0);
+ CKERR(r);
+
+
+ DBT key1, val1;
+ DBT key2, val2;
+
+ int64_t k, v;
+
+ dbt_init_realloc(&key1);
+ dbt_init_realloc(&val1);
+
+ dbt_init(&key2, &k, sizeof(k));
+ dbt_init(&val2, &v, sizeof(v));
+
+// k = firstkey;
+// v = generate_val(k);
+// r1 = c1->c_get(c1, &key2, &val2, DB_SET);
+// CKERR(r1);
+
+ int64_t i;
+ for (i = 0; i<numkeys; i++) {
+ k = i + firstkey;
+ v = generate_val(k);
+ r1 = c1->c_get(c1, &key1, &val1, DB_NEXT);
+// printf("k = %" PRIu64 ", v = %" PRIu64 ", key = %" PRIu64 ", val = %" PRIu64 "\n",
+// k, v, *((int64_t *)(key1.data)), *((int64_t *)(val1.data)));
+ assert(r1==0);
+ rval = verify_identical_dbts(&key1, &key2) |
+ verify_identical_dbts(&val1, &val2);
+ assert(rval == 0);
+ }
+ // now verify that there are no rows after the last expected
+ r1 = c1->c_get(c1, &key1, &val1, DB_NEXT);
+ assert(r1 == DB_NOTFOUND);
+
+ c1->c_close(c1);
+ if (key1.data) toku_free(key1.data);
+ if (val1.data) toku_free(val1.data);
+ compare_txn->commit(compare_txn, 0);
+}
+
+static void UU() crash_it(void) {
+ fflush(stdout);
+ fflush(stderr);
+ int zero = 0;
+ int divide_by_zero = 1/zero;
+ printf("force use of %d\n", divide_by_zero);
+ fflush(stdout);
+ fflush(stderr);
+}
+
+static void crash_it_callback_f(void *dummy UU()) {
+ crash_it();
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recovery_fileops_unit.cc b/storage/tokudb/PerconaFT/src/tests/recovery_fileops_unit.cc
new file mode 100644
index 00000000000..a4dc0ea9236
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recovery_fileops_unit.cc
@@ -0,0 +1,643 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+
+static int do_recover;
+static int do_crash;
+static char fileop;
+static int choices['I'-'A'+1];
+const int num_choices = sizeof(choices)/sizeof(choices[0]);
+static DB_TXN *txn;
+const char *oldname = "oldfoo";
+const char *newname = "newfoo";
+DB_ENV *env;
+DB *db;
+static int crash_during_checkpoint;
+static char *cmd;
+
+static void
+usage(void) {
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] (-c|-r) -O fileop -A# -B# -C# -D# -E# -F# [-G# -H# -I#]\n"
+ " fileop = c/r/d (create/rename/delete)\n"
+ " Where # is a single digit number > 0.\n"
+ " A-F are required for fileop=create\n"
+ " A-I are required for fileop=delete, fileop=rename\n", cmd);
+ exit(1);
+}
+
+
+enum { CLOSE_TXN_COMMIT, CLOSE_TXN_ABORT, CLOSE_TXN_NONE };
+enum {CREATE_CREATE, CREATE_CHECKPOINT, CREATE_COMMIT_NEW,
+ CREATE_COMMIT_NEW_CHECKPOINT, CREATE_COMMIT_CHECKPOINT_NEW,
+ CREATE_CHECKPOINT_COMMIT_NEW};
+
+static int fileop_did_commit(void);
+static void close_txn(int type);
+
+static int
+get_x_choice(char c, int possibilities) {
+ assert(c < 'A' + num_choices);
+ assert(c >= 'A');
+ int choice = choices[c-'A'];
+ if (choice >= possibilities)
+ usage();
+ return choice;
+}
+
+//return 0 or 1
+static int
+get_bool_choice(char c) {
+ return get_x_choice(c, 2);
+}
+
+static int
+get_choice_first_create_unrelated_txn(void) {
+ return get_bool_choice('A');
+}
+
+static int
+get_choice_do_checkpoint_after_fileop(void) {
+ return get_bool_choice('B');
+}
+
+static int
+get_choice_txn_close_type(void) {
+ return get_x_choice('C', 3);
+}
+
+static int
+get_choice_close_txn_before_checkpoint(void) {
+ int choice = get_bool_choice('D');
+ //Can't do checkpoint related thing without checkpoint
+ if (choice)
+ assert(get_choice_do_checkpoint_after_fileop());
+ return choice;
+}
+
+static int
+get_choice_crash_checkpoint_in_callback(void) {
+ int choice = get_bool_choice('E');
+ //Can't do checkpoint related thing without checkpoint
+ if (choice)
+ assert(get_choice_do_checkpoint_after_fileop());
+ return choice;
+}
+
+static int
+get_choice_flush_log_before_crash(void) {
+ return get_bool_choice('F');
+}
+
+static int
+get_choice_create_type(void) {
+ return get_x_choice('G', 6);
+}
+
+static int
+get_choice_txn_does_open_close_before_fileop(void) {
+ return get_bool_choice('H');
+}
+
+static int
+get_choice_lock_table_split_fcreate(void) {
+ int choice = get_bool_choice('I');
+ if (choice)
+ assert(fileop_did_commit());
+ return choice;
+}
+
+static void
+do_args(int argc, char * const argv[]) {
+ cmd = argv[0];
+ int i;
+ //Clear
+ for (i = 0; i < num_choices; i++) {
+ choices[i] = -1;
+ }
+
+ char c;
+ while ((c = getopt(argc, argv, "vqhcrO:A:B:C:D:E:F:G:H:I:X:")) != -1) {
+ switch(c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'q':
+ verbose--;
+ if (verbose<0) verbose=0;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ break;
+ case 'c':
+ do_crash = 1;
+ break;
+ case 'r':
+ do_recover = 1;
+ break;
+ case 'O':
+ if (fileop != '\0')
+ usage();
+ fileop = optarg[0];
+ switch (fileop) {
+ case 'c':
+ case 'r':
+ case 'd':
+ break;
+ default:
+ usage();
+ break;
+ }
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ if (fileop == '\0')
+ usage();
+ int num;
+ num = atoi(optarg);
+ if (num < 0 || num > 9)
+ usage();
+ choices[c - 'A'] = num;
+ break;
+ case 'X':
+ if (strcmp(optarg, "novalgrind") == 0) {
+ // provide a way for the shell script runner to pass an
+ // arg that suppresses valgrind on this child process
+ break;
+ }
+ // otherwise, fall through to an error
+ default:
+ usage();
+ break;
+ }
+ }
+ if (argc!=optind) { usage(); exit(1); }
+
+ for (i = 0; i < num_choices; i++) {
+ if (i >= 'G' - 'A' && fileop == 'c')
+ break;
+ if (choices[i] == -1)
+ usage();
+ }
+ assert(!do_recover || !do_crash);
+ assert(do_recover || do_crash);
+}
+
+static void UU() crash_it(void) {
+ int r;
+ if (get_choice_flush_log_before_crash()) {
+ r = env->log_flush(env, NULL); //TODO: USe a real DB_LSN* instead of NULL
+ CKERR(r);
+ }
+ fprintf(stderr, "HAPPY CRASH\n");
+ fflush(stdout);
+ fflush(stderr);
+ toku_hard_crash_on_purpose();
+ printf("This line should never be printed\n");
+ fflush(stdout);
+}
+
+static void checkpoint_callback_maybe_crash(void * UU(extra)) {
+ if (crash_during_checkpoint)
+ crash_it();
+}
+
+static void env_startup(void) {
+ int r;
+ int recover_flag = do_crash ? 0 : DB_RECOVER;
+ if (do_crash) {
+ db_env_set_checkpoint_callback(checkpoint_callback_maybe_crash, NULL);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ }
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | recover_flag;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ //Disable auto-checkpointing.
+ r = env->checkpointing_set_period(env, 0);
+ CKERR(r);
+}
+
+static void
+env_shutdown(void) {
+ int r;
+ r = env->close(env, 0);
+ CKERR(r);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+}
+
+static void
+checkpoint(void) {
+ int r;
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+}
+
+static void
+maybe_make_oldest_living_txn(void) {
+ if (get_choice_first_create_unrelated_txn()) {
+ // create a txn that never closes, forcing recovery to run from the beginning of the log
+ DB_TXN *oldest_living_txn;
+ int r;
+ r = env->txn_begin(env, NULL, &oldest_living_txn, 0);
+ CKERR(r);
+ checkpoint();
+ }
+}
+
+static void
+make_txn(void) {
+ int r;
+ assert(!txn);
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+}
+
+static void
+fcreate(void) {
+ int r;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, oldname, NULL, DB_BTREE, DB_CREATE|DB_EXCL, 0666);
+ CKERR(r);
+
+ if (fileop!='c' && get_choice_lock_table_split_fcreate()) {
+ r = db->close(db, 0);
+ CKERR(r);
+ close_txn(CLOSE_TXN_COMMIT);
+ make_txn();
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, oldname, NULL, DB_BTREE, 0, 0666);
+ CKERR(r);
+ r = db->pre_acquire_table_lock(db, txn);
+ CKERR(r);
+ }
+
+ DBT key, val;
+ dbt_init(&key, choices, sizeof(choices));
+ dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ dbt_init(&key, "name", sizeof("name"));
+ dbt_init(&val, (void*)oldname, strlen(oldname)+1);
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+
+ dbt_init(&key, "to_delete", sizeof("to_delete"));
+ dbt_init(&val, "delete_me", sizeof("delete_me"));
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ r = db->del(db, txn, &key, DB_DELETE_ANY);
+ CKERR(r);
+
+ dbt_init(&key, "to_delete2", sizeof("to_delete2"));
+ dbt_init(&val, "delete_me2", sizeof("delete_me2"));
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ r = db->del(db, txn, &key, 0);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void
+fdelete(void) {
+ int r;
+ r = env->dbremove(env, txn, oldname, NULL, 0);
+ CKERR(r);
+}
+
+static void
+frename(void) {
+ int r;
+ {
+ //Rename in 'key/val' pair.
+ DBT key,val;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, oldname, NULL, DB_BTREE, 0, 0666);
+ CKERR(r);
+ dbt_init(&key, "name", sizeof("name"));
+ dbt_init(&val, (void*)newname, strlen(newname)+1);
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+ }
+ r = env->dbrename(env, txn, oldname, NULL, newname, 0);
+ CKERR(r);
+}
+
+static void
+close_txn(int type) {
+ int r;
+ assert(txn);
+ if (type==CLOSE_TXN_COMMIT) {
+ //commit
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ txn = NULL;
+ }
+ else if (type == CLOSE_TXN_ABORT) {
+ //abort
+ r = txn->abort(txn);
+ CKERR(r);
+ txn = NULL;
+ }
+ else
+ assert(type == CLOSE_TXN_NONE);
+}
+
+static void
+create_and_crash(void) {
+ //Make txn
+ make_txn();
+ //fcreate
+ fcreate();
+
+ if (get_choice_do_checkpoint_after_fileop()) {
+ crash_during_checkpoint = get_choice_crash_checkpoint_in_callback();
+ if (get_choice_close_txn_before_checkpoint())
+ close_txn(get_choice_txn_close_type());
+ checkpoint();
+ if (!get_choice_close_txn_before_checkpoint())
+ close_txn(get_choice_txn_close_type());
+ }
+ else {
+ crash_during_checkpoint = get_choice_crash_checkpoint_in_callback();
+ assert(!crash_during_checkpoint);
+ close_txn(get_choice_txn_close_type());
+ }
+}
+
+static void
+create_and_maybe_checkpoint_and_or_close_after_create(void) {
+ fcreate();
+ switch (get_choice_create_type()) {
+ case (CREATE_CREATE): //Just create
+ break;
+ case (CREATE_CHECKPOINT): //Create then checkpoint
+ checkpoint();
+ break;
+ case (CREATE_COMMIT_NEW): //Create then commit
+ close_txn(CLOSE_TXN_COMMIT);
+ make_txn();
+ break;
+ case (CREATE_COMMIT_NEW_CHECKPOINT): //Create then commit then create new txn then checkpoint
+ close_txn(CLOSE_TXN_COMMIT);
+ make_txn();
+ checkpoint();
+ break;
+ case (CREATE_COMMIT_CHECKPOINT_NEW): //Create then commit then checkpoint then create new txn
+ close_txn(CLOSE_TXN_COMMIT);
+ checkpoint();
+ make_txn();
+ break;
+ case (CREATE_CHECKPOINT_COMMIT_NEW): //Create then checkpoint then commit then create new txn
+ checkpoint();
+ close_txn(CLOSE_TXN_COMMIT);
+ make_txn();
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+static void
+maybe_open_and_close_file_again_before_fileop(void) {
+ if (get_choice_txn_does_open_close_before_fileop()) {
+ int r;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, oldname, NULL, DB_BTREE, 0, 0666);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+ }
+}
+
+static void
+delete_and_crash(void) {
+ //Make txn
+ make_txn();
+ //fcreate
+ create_and_maybe_checkpoint_and_or_close_after_create();
+
+ maybe_open_and_close_file_again_before_fileop();
+
+ fdelete();
+ if (get_choice_do_checkpoint_after_fileop()) {
+ crash_during_checkpoint = get_choice_crash_checkpoint_in_callback();
+ if (get_choice_close_txn_before_checkpoint())
+ close_txn(get_choice_txn_close_type());
+ checkpoint();
+ if (!get_choice_close_txn_before_checkpoint())
+ close_txn(get_choice_txn_close_type());
+ }
+ else {
+ crash_during_checkpoint = get_choice_crash_checkpoint_in_callback();
+ assert(!crash_during_checkpoint);
+ close_txn(get_choice_txn_close_type());
+ }
+}
+
+static void
+rename_and_crash(void) {
+ //Make txn
+ make_txn();
+ //fcreate
+ create_and_maybe_checkpoint_and_or_close_after_create();
+
+ maybe_open_and_close_file_again_before_fileop();
+
+ frename();
+ if (get_choice_do_checkpoint_after_fileop()) {
+ crash_during_checkpoint = get_choice_crash_checkpoint_in_callback();
+ if (get_choice_close_txn_before_checkpoint())
+ close_txn(get_choice_txn_close_type());
+ checkpoint();
+ if (!get_choice_close_txn_before_checkpoint())
+ close_txn(get_choice_txn_close_type());
+ }
+ else {
+ crash_during_checkpoint = get_choice_crash_checkpoint_in_callback();
+ assert(!crash_during_checkpoint);
+ close_txn(get_choice_txn_close_type());
+ }
+}
+
+
+static void
+execute_and_crash(void) {
+ maybe_make_oldest_living_txn();
+ //split into create/delete/rename
+ if (fileop=='c')
+ create_and_crash();
+ else if (fileop == 'd')
+ delete_and_crash();
+ else {
+ assert(fileop == 'r');
+ rename_and_crash();
+ }
+ crash_it();
+}
+
+static int
+did_create_commit_early(void) {
+ int r;
+ switch (get_choice_create_type()) {
+ case (CREATE_CREATE): //Just create
+ case (CREATE_CHECKPOINT): //Create then checkpoint
+ r = 0;
+ break;
+ case (CREATE_COMMIT_NEW): //Create then commit
+ case (CREATE_COMMIT_NEW_CHECKPOINT): //Create then commit then create new txn then checkpoint
+ case (CREATE_COMMIT_CHECKPOINT_NEW): //Create then commit then checkpoint then create new txn
+ case (CREATE_CHECKPOINT_COMMIT_NEW): //Create then checkpoint then commit then create new txn
+ r = 1;
+ break;
+ default:
+ assert(false);
+ }
+ return r;
+}
+
+static int
+getf_do_nothing(DBT const* UU(key), DBT const* UU(val), void* UU(extra)) {
+ return 0;
+}
+
+static void
+verify_file_exists(const char *name, int should_exist) {
+ int r;
+ make_txn();
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, name, NULL, DB_BTREE, 0, 0666);
+ if (should_exist) {
+ CKERR(r);
+ DBT key, val;
+ dbt_init(&key, choices, sizeof(choices));
+ dbt_init(&val, NULL, 0);
+ r = db->get(db, txn, &key, &val, 0);
+ r = db->getf_set(db, txn, 0, &key, getf_do_nothing, NULL);
+ CKERR(r);
+ dbt_init(&key, "name", sizeof("name"));
+ dbt_init(&val, (void*)name, strlen(name)+1);
+ r = db->getf_set(db, txn, 0, &key, getf_do_nothing, NULL);
+ CKERR(r);
+
+ DBC *c;
+ r = db->cursor(db, txn, &c, 0);
+ CKERR(r);
+ int num_found = 0;
+ while ((r = c->c_getf_next(c, 0, getf_do_nothing, NULL)) == 0) {
+ num_found++;
+ }
+ CKERR2(r, DB_NOTFOUND);
+ assert(num_found == 2); //name and choices array.
+ r = c->c_close(c);
+ CKERR(r);
+ }
+ else
+ CKERR2(r, ENOENT);
+ r = db->close(db, 0);
+ CKERR(r);
+ close_txn(CLOSE_TXN_COMMIT);
+}
+
+static int
+fileop_did_commit(void) {
+ return get_choice_txn_close_type() == CLOSE_TXN_COMMIT &&
+ (!get_choice_do_checkpoint_after_fileop() ||
+ !get_choice_crash_checkpoint_in_callback() ||
+ get_choice_close_txn_before_checkpoint());
+}
+
+static void
+recover_and_verify(void) {
+ //Recovery was done during env_startup
+ int expect_old_name = 0;
+ int expect_new_name = 0;
+ if (fileop=='c') {
+ expect_old_name = fileop_did_commit();
+ }
+ else if (fileop == 'd') {
+ expect_old_name = did_create_commit_early() && !fileop_did_commit();
+ }
+ else {
+ //Wrong? if checkpoint AND crash during checkpoint
+ if (fileop_did_commit())
+ expect_new_name = 1;
+ else if (did_create_commit_early())
+ expect_old_name = 1;
+ }
+ verify_file_exists(oldname, expect_old_name);
+ verify_file_exists(newname, expect_new_name);
+ env_shutdown();
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ crash_during_checkpoint = 0; //Do not crash during checkpoint (possibly during recovery).
+ do_args(argc, argv);
+ env_startup();
+ if (do_crash)
+ execute_and_crash();
+ else
+ recover_and_verify();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/recovery_stress.cc b/storage/tokudb/PerconaFT/src/tests/recovery_stress.cc
new file mode 100644
index 00000000000..b1f5f98a90a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/recovery_stress.cc
@@ -0,0 +1,584 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "checkpoint_test.h"
+
+
+static const uint64_t max_cachesize = 256 << 20;
+static const int NUM_DICTIONARIES = 1;
+
+static const int OPER_STEPS = 6;
+
+static const int ITERATIONS_PER_CRASH_IN_RECOVERY = 7;
+
+typedef enum __recovery_stress_steps
+{
+ PRE_PRE_STEP = 0,
+ PRE_CP_STEP,
+ PRE_POST_STEP,
+ CP_CP_STEP,
+ CP_POST_STEP,
+ POST_POST_STEP
+} STEP;
+//const int OPER_PER_STEP = 331;
+const int OPER_PER_STEP = 43;
+#define OPER_PER_ITER ( OPER_STEPS * OPER_PER_STEP )
+
+#define DBG(str) if (verbose) printf("%s:%25s: %s\n", __FILE__, __FUNCTION__, str)
+#define iDBG(iter) if (verbose) printf("%s:%25s: iter = %d\n", __FILE__, __FUNCTION__, iter)
+
+static int firstkey(int iter, int step) { return (iter * OPER_PER_ITER) + (step * OPER_PER_STEP); }
+
+//static toku_pthread_t thread;
+
+static void
+drop_dead(void) {
+ // deliberate zerodivide or sigsegv
+#if 0
+ fprintf(stderr, "HAPPY CRASH\n");
+#endif
+ fflush(stdout);
+ fflush(stderr);
+ int zero = 0;
+ int infinity = 1/zero;
+ printf("Survived zerodivide!\n");
+ fflush(stdout);
+ printf("Infinity = %d\n", infinity);
+ fflush(stdout);
+ void * intothevoid = NULL;
+ (*(int*)intothevoid)++;
+ printf("intothevoid = %p, infinity = %d\n", intothevoid, infinity);
+ printf("This line should never be printed\n");
+ fflush(stdout);
+}
+
+static void drop_dead_callback_f(void *dummy UU()) {
+ drop_dead();
+}
+
+static void verify (DICTIONARY dictionaries, int iter) {
+ int i, key;
+ DB *db;
+// iDBG(iter);
+ for (i=0;i<NUM_DICTIONARIES;i++) {
+ db = dictionaries[i].db;
+ if (iter == 1) {
+ key = firstkey(iter - 1, 0);
+ verify_sequential_rows(db, key, OPER_PER_ITER);
+ }
+ else if (iter == 2) {
+ key = firstkey(iter - 2, 0);
+ verify_sequential_rows(db, key, OPER_PER_ITER * 2);
+ }
+ else if (iter == 3) {
+ key = firstkey(iter - 3, 0);
+ verify_sequential_rows(db, key, OPER_PER_ITER * 3);
+ }
+ else if (iter > 3) {
+ key = firstkey(iter - 4, 0);
+ verify_sequential_rows(db, key, OPER_PER_ITER * 4);
+ }
+ }
+ return;
+}
+
+struct iteration_spec {
+ DICTIONARY dictionaries;
+ int iter;
+ STEP step;
+ DB_TXN *pre_pre_insert_commit;
+ DB_TXN *pre_cp_insert_commit;
+ DB_TXN *pre_post_insert_commit;
+ DB_TXN *cp_cp_insert_commit;
+ DB_TXN *cp_post_insert_commit;
+ DB_TXN *post_post_insert_commit;
+
+ DB_TXN *pre_pre_insert_abort;
+ DB_TXN *pre_cp_insert_abort;
+ DB_TXN *pre_post_insert_abort;
+ DB_TXN *cp_cp_insert_abort;
+ DB_TXN *cp_post_insert_abort;
+ DB_TXN *post_post_insert_abort;
+
+ DB_TXN *pre_insert_incmplt;
+ DB_TXN *cp_insert_incmplt;
+ DB_TXN *post_insert_incmplt;
+
+ DB_TXN *pre_pre_delete_commit;
+ DB_TXN *pre_cp_delete_commit;
+ DB_TXN *pre_post_delete_commit;
+ DB_TXN *cp_cp_delete_commit;
+ DB_TXN *cp_post_delete_commit;
+ DB_TXN *post_post_delete_commit;
+
+ DB_TXN *pre_pre_delete_abort;
+ DB_TXN *pre_cp_delete_abort;
+ DB_TXN *pre_post_delete_abort;
+ DB_TXN *cp_cp_delete_abort;
+ DB_TXN *cp_post_delete_abort;
+ DB_TXN *post_post_delete_abort;
+
+ DB_TXN *pre_delete_incmplt;
+ DB_TXN *cp_delete_incmplt;
+ DB_TXN *post_delete_incmplt;
+};
+typedef struct iteration_spec *ITER_SPEC;
+
+static void pre_checkpoint_acts(ITER_SPEC spec) {
+ int i;
+ DB *db;
+ DICTIONARY dictionaries = spec->dictionaries;
+ int iter = spec->iter;
+ assert(spec->step == PRE_PRE_STEP);
+ int key;
+ int r;
+
+// iDBG(iter);
+ for (i=0;i<NUM_DICTIONARIES;i++) {
+ db = dictionaries[i].db;
+
+
+ // ---- GOOD INSERTIONS FOR THIS ITERATION ----
+ // begin pre, commit pre
+ key = firstkey(iter, PRE_PRE_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_pre_insert_commit, 0); CKERR(r);
+ insert_n_fixed(db, NULL, spec->pre_pre_insert_commit, key, OPER_PER_STEP);
+ r = spec->pre_pre_insert_commit->commit(spec->pre_pre_insert_commit, 0); CKERR(r);
+
+ // begin pre, commit cp, post
+ key = firstkey(iter, PRE_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_cp_insert_commit, 0); CKERR(r);
+ insert_n_fixed(db, NULL, spec->pre_cp_insert_commit, key, OPER_PER_STEP);
+ key = firstkey(iter, PRE_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_post_insert_commit, 0); CKERR(r);
+ insert_n_fixed(db, NULL, spec->pre_post_insert_commit, key, OPER_PER_STEP);
+
+
+ // ---- ABORTED INSERTIONS THAT WOULD OVERWRITE PREVIOUS ITERATION ----
+ if ( iter > 0 ) {
+ // begin pre, abort pre
+ key = firstkey(iter - 1, PRE_PRE_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_pre_insert_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->pre_pre_insert_abort, key, OPER_PER_STEP);
+ r = spec->pre_pre_insert_abort->abort(spec->pre_pre_insert_abort); CKERR(r);
+ // begin pre, abort cp, post
+ key = firstkey(iter - 1, PRE_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_cp_insert_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->pre_cp_insert_abort, key, OPER_PER_STEP);
+ key = firstkey(iter - 1, PRE_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_post_insert_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->pre_post_insert_abort, key, OPER_PER_STEP);
+ }
+
+ // ---- INCOMPLETE INSERTIONS THAT WOULD OVERWRITE I-2 ITERATIONS AGO ----
+ if ( iter > 1 ) {
+ // begin pre, incomplete
+ key = firstkey(iter - 2, PRE_PRE_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_insert_incmplt, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->pre_insert_incmplt, key, OPER_PER_STEP);
+ }
+
+ // ---- ABORTED DELETES THAT WOULD DELETE I-3 ITERATIONS AGO ----
+ if ( iter > 2 ) {
+ // begin pre, abort pre
+ key = firstkey(iter - 3, PRE_PRE_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_pre_delete_abort, 0); CKERR(r);
+ delete_n(db, NULL, spec->pre_pre_delete_abort, key, OPER_PER_STEP, 0);
+ r = spec->pre_pre_delete_abort->abort(spec->pre_pre_delete_abort); CKERR(r);
+ // begin pre, abort cp, post
+ key = firstkey(iter - 3, PRE_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_cp_delete_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->pre_cp_delete_abort, key, OPER_PER_STEP);
+ key = firstkey(iter - 3, PRE_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_post_delete_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->pre_post_delete_abort, key, OPER_PER_STEP);
+ }
+
+ // ---- INCOMPLETE DELETES THAT WOULD DELETE I-4 ITERATIONS AGO ----
+ if ( iter > 3 ) {
+ // begin pre, incomplete
+ key = firstkey(iter - 4, PRE_PRE_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_delete_incmplt, 0); CKERR(r);
+ delete_n(db, NULL, spec->pre_delete_incmplt, key, OPER_PER_STEP, 0);
+ }
+
+ // ---- GOOD DELETES THAT REMOVE I-5 ITERATIONS AGO ----
+ if ( iter > 4 ) {
+ // begin pre, commit pre
+ key = firstkey(iter - 5, PRE_PRE_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_pre_delete_commit, 0); CKERR(r);
+ delete_n(db, NULL, spec->pre_pre_delete_commit, key, OPER_PER_STEP, 0);
+ r = spec->pre_pre_delete_commit->commit(spec->pre_pre_delete_commit, 0); CKERR(r);
+
+ // begin pre, commit cp, post
+ key = firstkey(iter - 5, PRE_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_cp_delete_commit, 0); CKERR(r);
+ delete_n(db, NULL, spec->pre_cp_delete_commit, key, OPER_PER_STEP, 0);
+ key = firstkey(iter - 5, PRE_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->pre_post_delete_commit, 0); CKERR(r);
+ delete_n(db, NULL, spec->pre_post_delete_commit, key, OPER_PER_STEP, 0);
+ }
+ }
+ return;
+}
+
+static void checkpoint_acts(ITER_SPEC spec) {
+ int i, r, key;
+ DB *db;
+ int iter = spec->iter;
+ DICTIONARY dictionaries = spec->dictionaries;
+ assert(spec->step == CP_CP_STEP);
+// iDBG(iter);
+ for (i=0;i<NUM_DICTIONARIES;i++) {
+ db = dictionaries[i].db;
+
+ // ---- GOOD INSERTIONS FOR THIS ITERATION ----
+ // begin pre, commit cp
+ r = spec->pre_cp_insert_commit->commit(spec->pre_cp_insert_commit, 0); CKERR(r);
+ // begin cp, commit cp
+ key = firstkey(iter, CP_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_cp_insert_commit, 0); CKERR(r);
+ insert_n_fixed(db, NULL, spec->cp_cp_insert_commit, key, OPER_PER_STEP);
+ r = spec->cp_cp_insert_commit->commit(spec->cp_cp_insert_commit, 0); CKERR(r);
+
+ // begin cp, commit post
+ key = firstkey(iter, CP_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_post_insert_commit, 0); CKERR(r);
+ insert_n_fixed(db, NULL, spec->cp_post_insert_commit, key, OPER_PER_STEP);
+
+ // ---- ABORTED INSERTIONS THAT WOULD OVERWRITE PREVIOUS ITERATION ----
+ if ( iter > 0 ) {
+ // begin pre, abort cp
+ r = spec->pre_cp_insert_abort->abort(spec->pre_cp_insert_abort); CKERR(r);
+ // begin cp, abort cp
+ key = firstkey(iter - 1, CP_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_cp_insert_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->cp_cp_insert_abort, key, OPER_PER_STEP);
+ r = spec->cp_cp_insert_abort->abort(spec->cp_cp_insert_abort); CKERR(r);
+ // begin cp, abort post
+ key = firstkey(iter - 1, CP_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_post_insert_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->cp_post_insert_abort, key, OPER_PER_STEP);
+ }
+
+ // ---- INCOMPLETE INSERTIONS THAT WOULD OVERWRITE I-2 ITERATIONS AGO ----
+ if ( iter > 1 ) {
+ // begin cp, incomplete
+ key = firstkey(iter - 2, CP_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_insert_incmplt, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->cp_insert_incmplt, key, OPER_PER_STEP);
+ }
+
+ // ---- ABORTED DELETES THAT WOULD DELETE I-3 ITERATIONS AGO ----
+ if ( iter > 2 ) {
+ // begin pre, abort cp
+ r = spec->pre_cp_delete_abort->abort(spec->pre_cp_delete_abort); CKERR(r);
+ // begin cp, abort cp
+ key = firstkey(iter - 3, CP_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_cp_delete_abort, 0); CKERR(r);
+ delete_n(db, NULL, spec->cp_cp_delete_abort, key, OPER_PER_STEP, 0);
+ r = spec->cp_cp_delete_abort->abort(spec->cp_cp_delete_abort); CKERR(r);
+ // begin cp, abort post
+ key = firstkey(iter - 3, CP_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_post_delete_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->cp_post_delete_abort, key, OPER_PER_STEP);
+ }
+
+ // ---- INCOMPLETE DELETES THAT WOULD DELETE I-4 ITERATIONS AGO ----
+ if ( iter > 3 ) {
+ // begin pre, incomplete
+ key = firstkey(iter - 4, CP_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_delete_incmplt, 0); CKERR(r);
+ delete_n(db, NULL, spec->cp_delete_incmplt, key, OPER_PER_STEP, 0);
+ }
+
+ // ---- GOOD DELETES THAT REMOVE I-5 ITERATIONS AGO ----
+ if ( iter > 4 ) {
+ // begin pre, commit cp
+ r = spec->pre_cp_delete_commit->commit(spec->pre_cp_delete_commit, 0); CKERR(r);
+ // begin cp, commit cp
+ key = firstkey(iter - 5, CP_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_cp_delete_commit, 0); CKERR(r);
+ delete_n(db, NULL, spec->cp_cp_delete_commit, key, OPER_PER_STEP, 0);
+ r = spec->cp_cp_delete_commit->commit(spec->cp_cp_delete_commit, 0); CKERR(r);
+
+ // begin cp, commit post
+ key = firstkey(iter - 5, CP_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->cp_post_delete_commit, 0); CKERR(r);
+ delete_n(db, NULL, spec->cp_post_delete_commit, key, OPER_PER_STEP, 0);
+ }
+ }
+ return;
+}
+
+static void post_checkpoint_acts(ITER_SPEC spec) {
+ int i, r, key;
+ DB *db;
+ int iter = spec->iter;
+ DICTIONARY dictionaries = spec->dictionaries;
+ assert(spec->step == POST_POST_STEP);
+// iDBG(iter);
+ for (i=0;i<NUM_DICTIONARIES;i++) {
+ db = dictionaries[i].db;
+
+ // ---- GOOD INSERTIONS FOR THIS ITERATION ----
+ // begin pre, commit post
+ r = spec->pre_post_insert_commit->commit(spec->pre_post_insert_commit, 0); CKERR(r);
+ // begin cp, commit post
+ r = spec->cp_post_insert_commit->commit(spec->cp_post_insert_commit, 0); CKERR(r);
+ // begin post, commit post
+ key = firstkey(iter, POST_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->post_post_insert_commit, 0); CKERR(r);
+ insert_n_fixed(db, NULL, spec->post_post_insert_commit, key, OPER_PER_STEP);
+ r = spec->post_post_insert_commit->commit(spec->post_post_insert_commit, 0); CKERR(r);
+
+ // ---- ABORTED INSERTIONS THAT WOULD OVERWRITE PREVIOUS ITERATION ----
+ if ( iter > 0 ) {
+ // begin pre, abort post
+ r = spec->pre_post_insert_abort->abort(spec->pre_post_insert_abort); CKERR(r);
+ // begin cp, abort post
+ r = spec->cp_post_insert_abort->abort(spec->cp_post_insert_abort); CKERR(r);
+ // begin post, abort post
+ key = firstkey(iter - 1, POST_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->post_post_insert_abort, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->post_post_insert_abort, key, OPER_PER_STEP);
+ r = spec->post_post_insert_abort->abort(spec->post_post_insert_abort); CKERR(r);
+ }
+
+ // ---- INCOMPLETE INSERTIONS THAT WOULD OVERWRITE I-2 ITERATIONS AGO ----
+ if ( iter > 1 ) {
+ // begin post, incomplete
+ key = firstkey(iter - 2, POST_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->post_insert_incmplt, 0); CKERR(r);
+ insert_n_broken(db, NULL, spec->post_insert_incmplt, key, OPER_PER_STEP);
+ }
+
+ // ---- ABORTED DELETES THAT WOULD DELETE I-3 ITERATIONS AGO ----
+ if ( iter > 2 ) {
+ // begin pre, abort post
+ r = spec->pre_post_delete_abort->abort(spec->pre_post_delete_abort); CKERR(r);
+ // begin cp, abort post
+ r = spec->cp_post_delete_abort->abort(spec->cp_post_delete_abort); CKERR(r);
+ // begin post, abort post
+ key = firstkey(iter - 3, POST_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->post_post_delete_abort, 0); CKERR(r);
+ delete_n(db, NULL, spec->post_post_delete_abort, key, OPER_PER_STEP, 0);
+ r = spec->post_post_delete_abort->abort(spec->post_post_delete_abort); CKERR(r);
+ }
+
+ // ---- INCOMPLETE DELETES THAT WOULD DELETE I-4 ITERATIONS AGO ----
+ if ( iter > 3 ) {
+ // begin post, incomplete
+ key = firstkey(iter - 4, POST_POST_STEP);
+ r = env->txn_begin(env, NULL, &spec->post_delete_incmplt, 0); CKERR(r);
+ delete_n(db, NULL, spec->post_delete_incmplt, key, OPER_PER_STEP, 0);
+ }
+
+ // ---- GOOD DELETES THAT REMOVE I-5 ITERATIONS AGO ----
+ if ( iter > 4 ) {
+ // begin pre, commit post
+ r = spec->pre_post_delete_commit->commit(spec->pre_post_delete_commit, 0); CKERR(r);
+ // begin cp, commit post
+ r = spec->cp_post_delete_commit->commit(spec->cp_post_delete_commit, 0); CKERR(r);
+ // begin post, commit post
+ key = firstkey(iter - 5, CP_CP_STEP);
+ r = env->txn_begin(env, NULL, &spec->post_post_delete_commit, 0); CKERR(r);
+ delete_n(db, NULL, spec->post_post_delete_commit, key, OPER_PER_STEP, 0);
+ r = spec->post_post_delete_commit->commit(spec->post_post_delete_commit, 0); CKERR(r);
+ }
+ }
+ return;
+}
+
+static void run_test (int iter) {
+
+ uint32_t flags = 0;
+ int i, r;
+
+ if (iter == 0)
+ dir_create(TOKU_TEST_FILENAME); // create directory if first time through
+
+ // Run with cachesize of 256 bytes per iteration
+ // to force lots of disk I/O
+ // (each iteration inserts about 4K rows/dictionary, 16 bytes/row, 4 dictionaries = 256K bytes inserted per iteration)
+ uint64_t cachebytes = 0; // 0 => use default size
+ const int32_t K256 = 256 * 1024;
+ cachebytes = K256 * (iter + 1) - (128 * 1024);
+ if (cachebytes > max_cachesize)
+ cachebytes = 0;
+ if (iter & 2) cachebytes = 0; // use default cachesize half the time
+
+
+ if (verbose) printf("%s: iter = %d\n", __FILE__, iter);
+
+ int recovery_flags = DB_INIT_LOG|DB_INIT_TXN;
+ if ( iter != 0 )
+ recovery_flags += DB_RECOVER;
+
+ // crash somewhat frequently during recovery
+ // first, wait until after the system is primed
+ if ( iter > ITERATIONS_PER_CRASH_IN_RECOVERY + 5 ) {
+ // every N cycles, crash in recovery
+ if ( (iter % ITERATIONS_PER_CRASH_IN_RECOVERY) == 0 ) {
+ // crash at different places in recovery
+ if ( iter & 1 )
+ db_env_set_recover_callback(drop_dead_callback_f, NULL);
+ else
+ db_env_set_recover_callback2(drop_dead_callback_f, NULL);
+ }
+ }
+
+ env_startup(TOKU_TEST_FILENAME, cachebytes, recovery_flags);
+
+ // logic below counts on a mapping of 'iter' to dictionary values
+ // since crashes in recovery do not modify dictionary values
+ // need to adjust 'iter' to be iter of successful recoveries
+ int crashes_in_recovery = (iter / ITERATIONS_PER_CRASH_IN_RECOVERY) - ( ( ITERATIONS_PER_CRASH_IN_RECOVERY + 5 ) / ITERATIONS_PER_CRASH_IN_RECOVERY );
+ if ( crashes_in_recovery > 0 ) {
+ iter = iter - crashes_in_recovery;
+ }
+
+ // create array of dictionaries
+ // for each dictionary verify previous iterations and perform new inserts
+
+ DICTIONARY_S dictionaries[NUM_DICTIONARIES];
+ for (i = 0; i < NUM_DICTIONARIES; i++) {
+ char name[32];
+ sprintf(name, "stress_%d", i);
+ init_dictionary(&dictionaries[i], flags, name);
+ db_startup(&dictionaries[i], NULL);
+ }
+
+ // verify previous results
+ verify(dictionaries, iter);
+
+ struct iteration_spec spec;
+ spec.iter = iter;
+ spec.dictionaries = dictionaries;
+ spec.step = PRE_PRE_STEP;
+ // perform pre-checkpoint actions
+ pre_checkpoint_acts(&spec);
+
+ // perform checkpoint acts
+ spec.step = CP_CP_STEP;
+ if ( iter & 1 )
+ db_env_set_checkpoint_callback((void (*)(void*))checkpoint_acts, &spec);
+ else
+ db_env_set_checkpoint_callback2((void (*)(void*))checkpoint_acts, &spec);
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+ db_env_set_checkpoint_callback(NULL, NULL);
+ db_env_set_checkpoint_callback2(NULL, NULL);
+
+ // post checkpoint acts
+ spec.step = POST_POST_STEP;
+ post_checkpoint_acts(&spec);
+
+ // if requesting crash, randomly do other non-committed acts, then "drop_dead"
+ if (iter > 0) {
+ if (verbose) printf("dying\n");
+#if 0
+ // separate thread will perform random acts on other dictionaries (not 0)
+ r = toku_pthread_create(&thread, 0, random_acts, (void *) dictionaries);
+ CKERR(r);
+ // this thead will scribble over dictionary 0 before crash to verify that
+ // post-checkpoint inserts are not in the database
+ DB* db = dictionaries[0].db;
+ if (iter & 1)
+ scribble(db, iter);
+ else
+ thin_out(db, iter);
+#endif
+ uint32_t delay = myrandom();
+ delay &= 0xFFF; // select lower 12 bits, shifted up 8 for random number ...
+ delay = delay << 8; // ... uniformly distributed between 0 and 1M ...
+ usleep(delay); // ... to sleep up to one second (1M usec)
+ drop_dead();
+ }
+
+ for (i = 0; i < NUM_DICTIONARIES; i++) {
+ db_shutdown(&dictionaries[i]);
+ }
+ r = env->close(env, 0);
+ assert((r == 0) || (r == EINVAL)); // OK to have open transactions prior to close
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char *const argv[]);
+
+static int iter_arg = 0;
+
+int test_main(int argc, char *const*argv) {
+ do_args(argc, argv);
+ run_test(iter_arg);
+ return 0;
+}
+
+static void do_args(int argc, char *const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] [-i] [-C] \n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-i")==0) {
+ argc--; argv++;
+ iter_arg = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/redirect.cc b/storage/tokudb/PerconaFT/src/tests/redirect.cc
new file mode 100644
index 00000000000..f684982c870
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/redirect.cc
@@ -0,0 +1,327 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/*
+ - ydb layer test of redirection:
+ - create two dictionaries, close
+ - create txn
+ - open dictionary A
+ - redirect (using test-only wrapper in ydb)
+ - verify now open to dictionary B
+ - abort
+ - verify now open to dictionary A
+*/
+
+/*
+ for N = 0 .. n
+ for X == 0 .. x
+ for Y == 0 .. N+X
+ for c == 0 .. 1
+ create two dictionaries (iname A,B), close.
+ create txn
+ Open N DB handles to dictionary A
+ redirect from A to B
+ open X more DB handles to dictionary B
+ close Y DB handles to dictionary B
+ if c ==1 commit else abort
+*/
+
+#define DICT_0 "dict_0.db"
+#define DICT_1 "dict_1.db"
+enum {MAX_DBS = 3};
+static DB_ENV *env = NULL;
+static DB_TXN *txn = NULL;
+static DB *dbs[MAX_DBS];
+static int num_open_dbs = 0;
+static const char *dname = DICT_0;
+static DBT key;
+
+
+static void start_txn(void);
+static void commit_txn(void);
+static void open_db(void);
+static void close_db(void);
+static void insert(int index, int64_t i);
+static void
+start_env(void) {
+ assert(env==NULL);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ dname = DICT_0;
+
+ dbt_init(&key, "key", strlen("key")+1);
+
+ start_txn();
+ open_db();
+ insert(0, 0);
+ dname = DICT_1;
+ open_db();
+ insert(1, 1);
+ close_db();
+ close_db();
+ commit_txn();
+
+ dname = DICT_0;
+}
+
+static void
+end_env(void) {
+ int r;
+ r=env->close(env, 0);
+ CKERR(r);
+ env = NULL;
+}
+
+static void
+start_txn(void) {
+ assert(env!=NULL);
+ assert(txn==NULL);
+ int r;
+ r=env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+}
+
+static void
+abort_txn(void) {
+ assert(env!=NULL);
+ assert(txn!=NULL);
+ int r;
+ r=txn->abort(txn);
+ CKERR(r);
+ txn = NULL;
+}
+
+static void
+commit_txn(void) {
+ assert(env!=NULL);
+ assert(txn!=NULL);
+ int r;
+ r=txn->commit(txn, 0);
+ CKERR(r);
+ txn = NULL;
+}
+
+static void
+open_db(void) {
+ assert(env!=NULL);
+ assert(txn!=NULL);
+ assert(num_open_dbs < MAX_DBS);
+ assert(dbs[num_open_dbs] == NULL);
+
+ int r;
+
+ r = db_create(&dbs[num_open_dbs], env, 0);
+ CKERR(r);
+
+ DB *db = dbs[num_open_dbs];
+
+ r=db->open(db, txn, dname, 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ num_open_dbs++;
+}
+
+static void
+close_db(void) {
+ assert(env!=NULL);
+ assert(num_open_dbs > 0);
+ assert(dbs[num_open_dbs-1] != NULL);
+
+ num_open_dbs--;
+ int r;
+ DB *db = dbs[num_open_dbs];
+ r = db->close(db, 0);
+ CKERR(r);
+ dbs[num_open_dbs] = NULL;
+}
+
+static void
+insert(int idx, int64_t i) {
+ assert(env!=NULL);
+ assert(txn!=NULL);
+ assert(idx>=0);
+ assert(idx<num_open_dbs);
+
+ DB *db = dbs[idx];
+ DBT val;
+ dbt_init(&val, &i, sizeof(i));
+ int r=db->put(db, txn,
+ &key,
+ &val,
+ 0);
+ CKERR(r);
+}
+
+//Verify that ALL dbs point to expected dictionary.
+static void
+verify(int64_t i) {
+ assert(env!=NULL);
+ assert(txn!=NULL);
+ int r;
+ int which;
+ for (which = 0; which < num_open_dbs; which++) {
+ DB *db = dbs[which];
+ assert(db);
+ DBT val_expected, val_observed;
+ dbt_init(&val_expected, &i, sizeof(i));
+ dbt_init(&val_observed, NULL, 0);
+ r = db->get(db, txn, &key, &val_observed, 0);
+ CKERR(r);
+ r = int64_dbt_cmp(db, &val_expected, &val_observed);
+ assert(r==0);
+ }
+}
+
+static void
+redirect_dictionary(const char *new_dname, int r_expect) {
+ assert(env!=NULL);
+ assert(txn!=NULL);
+ assert(num_open_dbs>0);
+ int r;
+ DB *db = dbs[0];
+ assert(db!=NULL);
+ r = toku_test_db_redirect_dictionary(db, new_dname, txn); // ydb-level wrapper gets iname of new file and redirects
+ CKERR2(r, r_expect);
+ if (r==0) {
+ dname = new_dname;
+ }
+}
+
+static void
+redirect_EINVAL(void) {
+ start_env();
+ start_txn();
+ dname = DICT_0;
+ open_db();
+ dname = DICT_1;
+ open_db();
+ redirect_dictionary(DICT_1, EINVAL);
+ insert(1, 1);
+ redirect_dictionary(DICT_1, EINVAL);
+ close_db();
+ redirect_dictionary(DICT_1, EINVAL);
+ close_db();
+ commit_txn();
+ end_env();
+}
+
+static void
+redirect_test(uint8_t num_open_before, uint8_t num_open_after, uint8_t num_close_after, uint8_t commit) {
+ int i;
+ start_env();
+ start_txn();
+
+ assert(num_open_before > 0);
+
+ for (i = 0; i < num_open_before; i++) {
+ open_db();
+ }
+ verify(0);
+ redirect_dictionary(DICT_1, 0);
+ verify(1);
+ for (i = 0; i < num_open_after; i++) {
+ open_db();
+ }
+ verify(1);
+ assert(num_close_after <= num_open_before + num_open_after);
+ for (i = 0; i < num_close_after; i++) {
+ close_db();
+ }
+ verify(1);
+ if (commit) {
+ commit_txn();
+ start_txn();
+ verify(1);
+ commit_txn();
+ {
+ //Close any remaining open dbs.
+ int still_open = num_open_dbs;
+ assert(still_open == (num_open_before + num_open_after) - num_close_after);
+ for (i = 0; i < still_open; i++) {
+ close_db();
+ }
+ }
+ }
+ else {
+ {
+ //Close any remaining open dbs.
+ int still_open = num_open_dbs;
+ assert(still_open == (num_open_before + num_open_after) - num_close_after);
+ for (i = 0; i < still_open; i++) {
+ close_db();
+ }
+ }
+ abort_txn();
+ start_txn();
+ verify(0);
+ commit_txn();
+ }
+ end_env();
+}
+
+
+int
+test_main (int argc, char *const argv[])
+{
+ parse_args(argc, argv);
+ redirect_EINVAL();
+ int num_open_before; // number of dbs open before redirect
+ int num_open_after; // number of dbs opened after redirect
+ int num_close_after; // number of dbs closed after redirect
+ int commit;
+ for (num_open_before = 1; num_open_before <= 2; num_open_before++) {
+ for (num_open_after = 0; num_open_after <= 1; num_open_after++) {
+ for (num_close_after = 0; num_close_after <= num_open_before+num_open_after; num_close_after++) {
+ for (commit = 0; commit <= 1; commit++) {
+ redirect_test(num_open_before, num_open_after, num_close_after, commit);
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/replace-into-write-lock.cc b/storage/tokudb/PerconaFT/src/tests/replace-into-write-lock.cc
new file mode 100644
index 00000000000..8835cebc40d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/replace-into-write-lock.cc
@@ -0,0 +1,102 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that a db->put with NOOVERWRITE grabs a write lock not a read lock.
+// we use two transactions. the first transaction tries to put with NOOVERWRITE
+// and finds that the key already exists. it now holds a write lock on the key.
+// the second transaction trys to put the same key with NOOVERWRITE and gets
+// LOCK_NOTGRANTED. the second transaction can not put the key until the first
+// transaction commits.
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+
+ const char *env_dir = TOKU_TEST_FILENAME;
+ const char *db_filename = "replacetest";
+
+ parse_args(argc, argv);
+
+ char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
+ r = system(rm_cmd); assert_zero(r);
+
+ r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ r = env->open(env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+ DB_TXN *create_txn = NULL;
+ r = env->txn_begin(env, NULL, &create_txn, 0); assert_zero(r);
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+ r = create_txn->commit(create_txn, 0); assert_zero(r);
+
+ DB_TXN *write_txn = NULL;
+ r = env->txn_begin(env, NULL, &write_txn, 0); assert_zero(r);
+
+ int k = htonl(42); int v = 42;
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v, sizeof v);
+ r = db->put(db, write_txn, &key, &val, DB_NOOVERWRITE); assert_zero(r);
+ r = write_txn->commit(write_txn, 0); assert_zero(r);
+
+ DB_TXN *txn1 = NULL;
+ r = env->txn_begin(env, NULL, &txn1, 0); assert_zero(r);
+
+ DB_TXN *txn2 = NULL;
+ r = env->txn_begin(env, NULL, &txn2, 0); assert_zero(r);
+
+ r = db->put(db, txn1, &key, &val, DB_NOOVERWRITE); assert(r == DB_KEYEXIST);
+ r = db->put(db, txn2, &key, &val, DB_NOOVERWRITE); assert(r == DB_LOCK_NOTGRANTED);
+ r = db->put(db, txn1, &key, &val, 0); assert_zero(r);
+ r = db->put(db, txn2, &key, &val, 0); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn1->commit(txn1, 0); assert_zero(r);
+ r = db->put(db, txn2, &key, &val, 0); assert_zero(r);
+ r = txn2->commit(txn2, 0); assert_zero(r);
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/root_fifo_1.cc b/storage/tokudb/PerconaFT/src/tests/root_fifo_1.cc
new file mode 100644
index 00000000000..7e9ace14a87
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/root_fifo_1.cc
@@ -0,0 +1,185 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test txn commit after db close
+
+#include "test.h"
+#include <sys/stat.h>
+
+DB_ENV *null_env = NULL;
+DB *null_db = NULL;
+DB_TXN *null_txn = NULL;
+DBC *null_cursor = NULL;
+int constant = 0;
+
+static void root_fifo_verify(DB_ENV *env, int n) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+ int r;
+
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBC *cursor = null_cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key, val;
+ memset(&key, 0, sizeof key); memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0) break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert((int)toku_ntohl(k) == i);
+ }
+ if (constant)
+ assert(i==1);
+ else
+ assert(i == n);
+
+ r = cursor->c_close(cursor); assert(r == 0); cursor = null_cursor;
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+}
+
+static void root_fifo_1(int n, int create_outside) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+ int r;
+
+ // create the env
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env = null_env;
+ r = db_env_create(&env, 0); assert(r == 0); assert(env != NULL);
+ r = env->open(env,
+ TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL+DB_INIT_LOG+DB_INIT_LOCK+DB_INIT_TXN+DB_PRIVATE+DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ if (create_outside) {
+ DB_TXN *txn_open = null_txn;
+ r = env->txn_begin(env, null_txn, &txn_open, 0); assert(r == 0); assert(txn_open != NULL);
+ DB *db_open = null_db;
+ r = db_create(&db_open, env, 0); assert(r == 0); assert(db_open != NULL);
+ r = db_open->open(db_open, txn_open, "test.db", 0, DB_BTREE, DB_CREATE|DB_EXCL, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+ r = db_open->close(db_open, 0); assert(r == 0); db_open = null_db;
+ r = txn_open->commit(txn_open, 0); assert(r == 0); txn_open = null_txn;
+ }
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ int i;
+ for (i=0; i<n; i++) {
+ if (verbose>1) {
+ printf("%s-%s:%d %d\n", __FILE__, __FUNCTION__, __LINE__, i);
+ }
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBT key, val;
+ int k = toku_htonl(i);
+ int v = i;
+ if (constant) {
+ k = v = 0;
+ }
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ // verify the db
+ root_fifo_verify(env, n);
+
+ // cleanup
+ r = env->close(env, 0); assert(r == 0); env = null_env;
+}
+
+int test_main(int argc, char *const argv[]) {
+ int i;
+ int n = -1;
+
+ // parse_args(argc, argv);
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-n") == 0) {
+ if (i+1 < argc)
+ n = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ verbose--;
+ if (verbose<0) verbose = 0;
+ continue;
+ }
+ if (strcmp(argv[i], "-c") == 0) {
+ constant = 1;
+ continue;
+ }
+ }
+
+ if (n >= 0) {
+ root_fifo_1(n, 0);
+ root_fifo_1(n, 1);
+ }
+ else
+ for (i=0; i<100; i++) {
+ root_fifo_1(i, 0);
+ root_fifo_1(i, 1);
+ }
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/root_fifo_2.cc b/storage/tokudb/PerconaFT/src/tests/root_fifo_2.cc
new file mode 100644
index 00000000000..a240e1e70f6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/root_fifo_2.cc
@@ -0,0 +1,166 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test txn commit after db close
+
+#include "test.h"
+#include <sys/stat.h>
+
+DB_ENV *null_env = NULL;
+DB *null_db = NULL;
+DB_TXN *null_txn = NULL;
+DBC *null_cursor = NULL;
+
+static void root_fifo_verify(DB_ENV *env, int n) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+ int r;
+
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBC *cursor = null_cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key, val;
+ memset(&key, 0, sizeof key); memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0) break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert((int)toku_ntohl(k) == i);
+ }
+ assert(i == 0);
+
+ r = cursor->c_close(cursor); assert(r == 0); cursor = null_cursor;
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+}
+
+static void root_fifo_2(int n, int create_outside) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+ int r;
+
+ // create the env
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env = null_env;
+ r = db_env_create(&env, 0); assert(r == 0); assert(env != NULL);
+ r = env->open(env,
+ TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL+DB_INIT_LOG+DB_INIT_LOCK+DB_INIT_TXN+DB_PRIVATE+DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ if (create_outside) {
+ DB_TXN *txn_open = null_txn;
+ r = env->txn_begin(env, null_txn, &txn_open, 0); assert(r == 0); assert(txn_open != NULL);
+ DB *db_open = null_db;
+ r = db_create(&db_open, env, 0); assert(r == 0); assert(db_open != NULL);
+ r = db_open->open(db_open, txn_open, "test.db", 0, DB_BTREE, DB_CREATE|DB_EXCL, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+ r = db_open->close(db_open, 0); assert(r == 0); db_open = null_db;
+ r = txn_open->commit(txn_open, 0); assert(r == 0); txn_open = null_txn;
+ }
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ int i;
+ for (i=0; i<n; i++) {
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBT key, val;
+ int k = toku_htonl(i);
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+ }
+
+ r = txn->abort(txn); assert(r == 0); txn = null_txn;
+
+ // verify the db
+ root_fifo_verify(env, n);
+
+ // cleanup
+ r = env->close(env, 0);
+ assert(r == 0); env = null_env;
+}
+
+int test_main(int argc, char *const argv[]) {
+ int i;
+ int n = -1;
+
+ // parse_args(argc, argv);
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose = 1;
+ continue;
+ }
+ if (strcmp(argv[i], "-n") == 0) {
+ if (i+1 < argc)
+ n = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ if (n >= 0) {
+ root_fifo_2(n, 0);
+ root_fifo_2(n, 1);
+ }
+ else
+ for (i=0; i<100; i++) {
+ root_fifo_2(i, 0);
+ root_fifo_2(i, 1);
+ }
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/root_fifo_31.cc b/storage/tokudb/PerconaFT/src/tests/root_fifo_31.cc
new file mode 100644
index 00000000000..1f4390af631
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/root_fifo_31.cc
@@ -0,0 +1,188 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test txn commit after db close
+
+#include "test.h"
+#include <sys/stat.h>
+
+DB_ENV *null_env = NULL;
+DB *null_db = NULL;
+DB_TXN *null_txn = NULL;
+DBC *null_cursor = NULL;
+
+static void create_non_empty(int n) {
+ DB_ENV *env = null_env;
+ int r;
+ r = db_env_create(&env, 0); assert(r == 0); assert(env != NULL);
+ r = env->open(env,
+ TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL+DB_INIT_LOG+DB_INIT_LOCK+DB_INIT_TXN+DB_PRIVATE+DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ int i;
+ for (i=n; i<2*n; i++) {
+ DBT key, val;
+ int k = toku_htonl(i);
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ r = env->close(env, 0); assert(r == 0); env = null_env;
+}
+
+static void root_fifo_verify(DB_ENV *env, int n) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+
+ int r;
+
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBC *cursor = null_cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key, val;
+ memset(&key, 0, sizeof key); memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0) break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert((int)toku_ntohl(k) == i);
+ }
+ assert(i == 2*n);
+
+ r = cursor->c_close(cursor); assert(r == 0); cursor = null_cursor;
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+}
+
+static void root_fifo_31(int n) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+ int r;
+
+ // create the env
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ // populate
+ create_non_empty(n);
+
+ DB_ENV *env = null_env;
+ r = db_env_create(&env, 0); assert(r == 0); assert(env != NULL);
+ r = env->open(env,
+ TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL+DB_INIT_LOG+DB_INIT_LOCK+DB_INIT_TXN+DB_PRIVATE+DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ int i;
+ for (i=0; i<n; i++) {
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBT key, val;
+ int k = toku_htonl(i);
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ // verify the db
+ root_fifo_verify(env, n);
+
+ // cleanup
+ r = env->close(env, 0); assert(r == 0); env = null_env;
+}
+
+int test_main(int argc, char *const argv[]) {
+ int i;
+ int n = -1;
+
+ // parse_args(argc, argv);
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose = 1;
+ continue;
+ }
+ if (strcmp(argv[i], "-n") == 0) {
+ if (i+1 < argc)
+ n = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ if (n >= 0)
+ root_fifo_31(n);
+ else
+ for (i=0; i<100; i++)
+ root_fifo_31(i);
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/root_fifo_32.cc b/storage/tokudb/PerconaFT/src/tests/root_fifo_32.cc
new file mode 100644
index 00000000000..fbaba4e3d5d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/root_fifo_32.cc
@@ -0,0 +1,188 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test txn commit after db close
+
+#include "test.h"
+#include <sys/stat.h>
+
+DB_ENV *null_env = NULL;
+DB *null_db = NULL;
+DB_TXN *null_txn = NULL;
+DBC *null_cursor = NULL;
+
+static void create_non_empty(int n) {
+ DB_ENV *env = null_env;
+ int r;
+ r = db_env_create(&env, 0); assert(r == 0); assert(env != NULL);
+ r = env->open(env,
+ TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL+DB_INIT_LOG+DB_INIT_LOCK+DB_INIT_TXN+DB_PRIVATE+DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ int i;
+ for (i=n; i<2*n; i++) {
+ DBT key, val;
+ int k = toku_htonl(i);
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ r = env->close(env, 0); assert(r == 0); env = null_env;
+}
+
+static void root_fifo_verify(DB_ENV *env, int n) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+
+ int r;
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBC *cursor = null_cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ int i;
+ for (i = n; ; i++) {
+ DBT key, val;
+ memset(&key, 0, sizeof key); memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0) break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert((int)toku_ntohl(k) == i);
+ }
+ assert(i == 2*n);
+
+ r = cursor->c_close(cursor); assert(r == 0); cursor = null_cursor;
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+}
+
+static void root_fifo_32(int n) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+ int r;
+
+ // create the env
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ // populate
+ create_non_empty(n);
+
+ DB_ENV *env = null_env;
+ r = db_env_create(&env, 0); assert(r == 0); assert(env != NULL);
+ r = env->open(env,
+ TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL+DB_INIT_LOG+DB_INIT_LOCK+DB_INIT_TXN+DB_PRIVATE+DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ int i;
+ for (i=0; i<n; i++) {
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBT key, val;
+ int k = toku_htonl(i);
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+ }
+
+ r = txn->abort(txn); assert(r == 0); txn = null_txn;
+
+ // verify the db
+ root_fifo_verify(env, n);
+
+ // cleanup
+ r = env->close(env, 0); assert(r == 0); env = null_env;
+}
+
+int test_main(int argc, char *const argv[]) {
+ int i;
+ int n = -1;
+
+ // parse_args(argc, argv);
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose = 1;
+ continue;
+ }
+ if (strcmp(argv[i], "-n") == 0) {
+ if (i+1 < argc)
+ n = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ if (n >= 0)
+ root_fifo_32(n);
+ else
+ for (i=0; i<100; i++)
+ root_fifo_32(i);
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/root_fifo_41.cc b/storage/tokudb/PerconaFT/src/tests/root_fifo_41.cc
new file mode 100644
index 00000000000..79e3de53314
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/root_fifo_41.cc
@@ -0,0 +1,230 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test txn commit after db close
+
+#include "test.h"
+#include <sys/stat.h>
+
+DB_ENV *null_env = NULL;
+DB *null_db = NULL;
+DB_TXN *null_txn = NULL;
+DBC *null_cursor = NULL;
+
+static void create_non_empty(int n, const char *dirname) {
+ DB_ENV *env = null_env;
+ int r;
+ r = db_env_create(&env, 0); assert(r == 0); assert(env != NULL);
+ r = env->set_redzone(env, 0); assert(r == 0);
+ r = env->open(env,
+ dirname,
+ DB_INIT_MPOOL+DB_INIT_LOG+DB_INIT_LOCK+DB_INIT_TXN+DB_PRIVATE+DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ int i;
+ for (i=n; i<2*n; i++) {
+ DBT key, val;
+ int k = toku_htonl(i);
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ r = env->close(env, 0); assert(r == 0); env = null_env;
+}
+
+static void root_fifo_verify(DB_ENV *env, int n, int expectn) {
+ if (verbose) printf("%s:%d %d %d\n", __FUNCTION__, __LINE__, n, expectn);
+
+ int r;
+ DB_TXN *txn = null_txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0); assert(txn != NULL);
+
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBC *cursor = null_cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key, val;
+ memset(&key, 0, sizeof key); memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0) break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert((int)toku_ntohl(k) == i);
+ }
+ assert(i == expectn);
+
+ r = cursor->c_close(cursor); assert(r == 0); cursor = null_cursor;
+
+ r = txn->commit(txn, 0); assert(r == 0); txn = null_txn;
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+}
+
+static void root_fifo_41(int n, int ntxn, bool do_populate) {
+ if (verbose) printf("%s:%d %d\n", __FUNCTION__, __LINE__, n);
+ int r;
+
+ const char *dirname = TOKU_TEST_FILENAME;
+
+ // create the env
+ toku_os_recursive_delete(dirname);
+ toku_os_mkdir(dirname, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ // populate
+ if (do_populate)
+ create_non_empty(n, dirname);
+
+ DB_ENV *env = null_env;
+ r = db_env_create(&env, 0); assert(r == 0); assert(env != NULL);
+ r = env->set_redzone(env, 0); assert(r == 0);
+ r = env->open(env,
+ dirname,
+ DB_INIT_MPOOL+DB_INIT_LOG+DB_INIT_LOCK+DB_INIT_TXN+DB_PRIVATE+DB_CREATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+ {
+ DB_TXN *txn;
+ DB *db = null_db;
+ r = env->txn_begin(env, null_txn, &txn, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ }
+
+ DB_TXN *txn[ntxn];
+ int i;
+ for (i=0; i<ntxn; i++) {
+ r = env->txn_begin(env, null_txn, &txn[i], 0); assert(r == 0); assert(txn[i] != NULL);
+ }
+
+ for (i=0; i<n; i++) {
+ DB *db = null_db;
+ r = db_create(&db, env, 0); assert(r == 0); assert(db != NULL);
+
+ r = db->open(db, txn[i % ntxn], "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DBT key, val;
+ int k = toku_htonl(i);
+ r = db->put(db, txn[i % ntxn], dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0); db = null_db;
+ }
+
+ for (i=0; i<ntxn; i++) {
+ r = txn[i]->commit(txn[i], 0); assert(r == 0);
+ }
+
+ // verify the db
+ root_fifo_verify(env, n, do_populate ? 2*n : n);
+
+ // cleanup
+ r = env->close(env, 0); assert(r == 0); env = null_env;
+}
+
+static int parseint (char const *str) {
+ char *end;
+ errno=0;
+ int v = strtol(str, &end, 10);
+ if (errno!=0 || *end!=0) {
+ fprintf(stderr, "This argument should be an int: %s\n", str);
+ exit(1);
+ }
+ return v;
+}
+
+int test_main(int argc, char *const argv[]) {
+ int i;
+ int n = -1;
+ int ntxn = -1;
+ bool do_populate = false;
+
+ // parse_args(argc, argv);
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose = 1;
+ } else if (strcmp(argv[i], "-n") == 0) {
+ assert(i+1 < argc);
+ n = parseint(argv[++i]);
+ } else if (strcmp(argv[i], "-ntxn") == 0) {
+ assert(i+1 < argc);
+ ntxn = parseint(argv[++i]);
+ } else if (strcmp(argv[i], "-populate") == 0) {
+ do_populate = true;
+ } else {
+ fprintf(stderr, "What is this argument? %s\n", argv[i]);
+ exit(1);
+ }
+ }
+
+ if (n >= 0)
+ root_fifo_41(n, ntxn == -1 ? 1 : ntxn, do_populate);
+ else {
+ for (i=0; i<100; i++) {
+ for (ntxn=1; ntxn<=4; ntxn++) {
+ root_fifo_41(i, ntxn, false);
+ root_fifo_41(i, ntxn, true);
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/rowsize.cc b/storage/tokudb/PerconaFT/src/tests/rowsize.cc
new file mode 100644
index 00000000000..1c5a1bb4ab3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/rowsize.cc
@@ -0,0 +1,91 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+static DB_ENV *env = NULL;
+static DB *db = NULL;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static void setup_env (void) {
+ const int len = strlen(envdir)+100;
+ char cmd[len];
+ snprintf(cmd, len, "rm -rf %s", envdir);
+ {int r = system(cmd); CKERR(r); }
+ {int r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r); }
+ {int r = db_env_create(&env, 0); CKERR(r); }
+ //env->set_errfile(env, stderr);
+ CKERR(env->set_redzone(env, 0));
+ { int r = env->open(env, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r); }
+ { int r = db_create(&db, env, 0); CKERR(r); }
+ { int r = db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r); }
+}
+
+static void shutdown_env (void) {
+ { int r = db->close(db, 0); CKERR(r); }
+ { int r = env->close(env, 0); CKERR(r); }
+}
+
+static void put (const char *keystring, int size, bool should_work) {
+ DBT k, v;
+ dbt_init(&k, keystring, 1+strlen(keystring));
+ dbt_init(&v, toku_xcalloc(size, 1), size);
+ static DB_TXN *txn = NULL;
+ { int r = env->txn_begin(env, 0, &txn, 0); CKERR(r); }
+ {
+ int r = db->put(db, NULL, &k, &v, 0);
+ if (should_work) {
+ CKERR(r);
+ } else {
+ assert(r!=0);
+ }
+ }
+ { int r = txn->commit(txn, 0); CKERR(r); }
+ toku_free(v.data);
+}
+
+int test_main (int argc, char *const argv[]) {
+ if (0) parse_args(argc, argv);
+ setup_env();
+ if (0) put("foo", 32, true);
+ put("foo", 32*1024*1024, true);
+ put("bar", 32*1024*1024+1, false);
+ shutdown_env();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/run-hotindexer-undo-do-tests.bash b/storage/tokudb/PerconaFT/src/tests/run-hotindexer-undo-do-tests.bash
new file mode 100755
index 00000000000..90382cbff82
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run-hotindexer-undo-do-tests.bash
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+# run a sequence of hotindexer undo tests.
+
+tests=""
+verbose=0
+valgrind=""
+exitcode=0
+
+for arg in $* ; do
+ if [[ $arg =~ --(.*)=(.*) ]] ; then
+ eval ${BASH_REMATCH[1]}=${BASH_REMATCH[2]}
+ else
+ tests="$tests $arg"
+ fi
+done
+
+for t in $tests ; do
+ testdir=`dirname $t`
+ testfile=`basename $t`
+ testname=""
+ resultfile=""
+ if [[ $testfile =~ (.*)\.test$ ]] ; then
+ testname=${BASH_REMATCH[1]}
+ resultfile=$testname.result
+ else
+ exit 1
+ fi
+ if [ $verbose != 0 ] ; then echo $testdir $testname $testfile $resultfile; fi
+
+ $valgrind ./hotindexer-undo-do-test.tdb $testdir/$testfile >$testdir/$testname.run
+
+ if [ -f $testdir/$resultfile ] ; then
+ diff -q $testdir/$testname.run $testdir/$resultfile >/dev/null 2>&1
+ exitcode=$?
+ else
+ exitcode=1
+ fi
+ if [ $verbose != 0 ] ; then
+ echo $testname $exitcode
+ else
+ rm $testdir/$testname.run
+ fi
+ if [ $exitcode != 0 ] ; then break; fi
+done
+
+exit $exitcode
diff --git a/storage/tokudb/PerconaFT/src/tests/run_abortrecover_test.sh b/storage/tokudb/PerconaFT/src/tests/run_abortrecover_test.sh
new file mode 100755
index 00000000000..78825603662
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_abortrecover_test.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+
+if [[ $# -ne 1 ]]; then exit 1; fi
+
+bin=$1; shift
+
+if $bin --test
+then
+ echo $bin --test did not crash
+ exit 1
+else
+ set -e
+ $bin --recover
+fi
diff --git a/storage/tokudb/PerconaFT/src/tests/run_checkpoint_stress_test.sh b/storage/tokudb/PerconaFT/src/tests/run_checkpoint_stress_test.sh
new file mode 100755
index 00000000000..b7cfe0f0ebb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_checkpoint_stress_test.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+set -e
+
+test $# -ge 4
+
+bin=$1; shift
+size=$1; shift
+runs=$1; shift
+abortcode=$1; shift
+
+$bin -C -n $size
+$bin -C -i 0 -n $size
+for (( i = 1; i < $runs; i++ ))
+do
+ echo -n "$i: " && date
+ set +e
+ $bin -c -i $i -n $size -X novalgrind 2>$TOKU_TEST_FILENAME/error.$i
+ test $? -eq $abortcode || exit 1
+ set -e
+ grep -q 'HAPPY CRASH' $TOKU_TEST_FILENAME/error.$i
+done
diff --git a/storage/tokudb/PerconaFT/src/tests/run_diskfull_test.sh b/storage/tokudb/PerconaFT/src/tests/run_diskfull_test.sh
new file mode 100755
index 00000000000..26172fecf5c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_diskfull_test.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+set -e
+
+test $# -ge 2
+
+bin=$1; shift
+abortcode=$1; shift
+
+num_writes=$($bin -q)
+set +e
+for (( i = 0; i < $num_writes; i++ ))
+do
+ $bin -C $i
+ test $? -eq $abortcode || exit 1
+done
diff --git a/storage/tokudb/PerconaFT/src/tests/run_powerfail_test.py b/storage/tokudb/PerconaFT/src/tests/run_powerfail_test.py
new file mode 100755
index 00000000000..41a07acb97b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_powerfail_test.py
@@ -0,0 +1,140 @@
+#!/usr/local/bin/python2.6
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+import sys
+import os
+import optparse
+import getpass
+import pexpect
+import time
+from multiprocessing import Process
+
+# options
+parser = optparse.OptionParser()
+parser.add_option('--test', dest='test', type='string', default=None, help="name of stress test to run")
+parser.add_option('--iterations', dest='iterations', type='int', default=1, help="Number of test iterations (default = 1)")
+parser.add_option('--verbose', dest='verbose', action="store_true", default=False, help="Verbose printing (default = FALSE)")
+parser.add_option('--client', dest='client', type='string', default='192.168.1.107', help='client machine being power failed (default=tick)')
+parser.add_option('--sandbox_dir', dest='sbdir', type='string', default='/tpch/mysb/msb_3.0.2-beta_16948/', help='sandbox directory (default = None)')
+options,remainder = parser.parse_args()
+
+nameaddr='mysql@'+options.client
+password='mytokudb'
+
+ipm_nameaddr='admn@192.168.1.254'
+ipm_passwd='admn'
+
+def IPM_cmd(cmds):
+ # password handling
+ ssh_newkey = 'Are you sure you want to continue connecting'
+ p=pexpect.spawn('ssh %s' % ipm_nameaddr, timeout=60)
+ i=p.expect([ssh_newkey,'Password:',pexpect.EOF])
+ if i==0:
+ p.sendline('yes')
+ i=p.expect([ssh_newkey,'Password:',pexpect.EOF])
+ if i==1:
+ p.sendline(ipm_passwd)
+ elif i==2:
+ print "I either got key or connection timeout"
+ pass
+
+ # run command(s)
+ i = p.expect('Sentry:')
+ for cmd in cmds:
+ if i==0:
+ p.sendline(cmd)
+ else:
+ print 'p.expect saw', p.before
+ i = p.expect('Sentry:')
+ print p.before
+
+ # close session
+ p.sendline('quit')
+ p.expect(pexpect.EOF)
+ return 0
+
+def IPM_power_on():
+ IPM_cmd(['on all'])
+
+def IPM_power_off():
+ IPM_cmd(['off all'])
+
+def ssh_cmd(cmd, verbose=True, timeout=30):
+
+ ssh_newkey = 'Are you sure you want to continue connecting'
+ p=pexpect.spawn('ssh %s %s' % (nameaddr, cmd), timeout=timeout)
+
+ i=p.expect([ssh_newkey,'password:',pexpect.EOF])
+ if i==0:
+ p.sendline('yes')
+ i=p.expect([ssh_newkey,'password:',pexpect.EOF])
+ if i==1:
+ if verbose:
+ print 'ssh %s %s' % (nameaddr, cmd)
+ p.sendline(password)
+ p.expect(pexpect.EOF)
+ elif i==2:
+ print "I either got key or connection timeout"
+ pass
+ if verbose:
+ print p.before
+ return p.before
+
+def client_cmd(cmd, verbose=True, timeout=3600):
+ ssh_cmd(cmd, verbose, timeout)
+
+def ping_server(name):
+ p=pexpect.spawn("ping -c 1 "+name)
+ i=p.expect(['1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms',
+ '1 packets transmitted, 1 received, 0% packet loss, time 0ms',
+ pexpect.EOF])
+ return i
+
+
+def test_it():
+ cmd = "/home/wells/svn/iibench/py/iibench.py --db_config_file=%smy.sandbox.cnf --max_rows=1000000000 --engine=tokudb --outfile=/tmp/pf_%d" % (options.sbdir, options.iterations)
+ print "CMD = ", cmd
+ client_cmd(cmd, timeout=3600)
+
+def run_test():
+# cmd = options.test
+# if ( options.verbose ): cmd += ' -v'
+# for i in range(options.iterations):
+
+ t0 = Process(target=test_it, args=())
+ for iter in range(options.iterations + 1):
+ print "Turn On Power to Server"
+ IPM_power_on()
+ i = ping_server(options.client)
+ while ( i != 1 ):
+ i = ping_server(options.client)
+ print "Server rebooted, wait 30 seconds to restart MySQL"
+ time.sleep(30)
+ print "Start MySQL"
+ client_cmd(options.sbdir+'stop') # clears out flags from previous start
+ client_cmd(options.sbdir+'start')
+ if iter < options.iterations:
+ print "Run Test"
+ t0.start()
+ print "Sleep(%d)" % (300 + iter)
+ time.sleep(300 + iter)
+ print "Turn Off Power to Server"
+ IPM_power_off()
+ t0.terminate()
+ else:
+ # last loop through, just cleanup
+ client_cmd(options.sbdir+'stop')
+
+def main(argv):
+ run_test()
+ return 0
+
+if __name__ == '__main__':
+ usage = sys.modules["__main__"].__doc__
+ parser.set_usage(usage)
+ unused_flags, new_argv = parser.parse_args(args=sys.argv[1:], values=options)
+ sys.exit(main([sys.argv[0]] + new_argv))
+
diff --git a/storage/tokudb/PerconaFT/src/tests/run_recover_stress_test.sh b/storage/tokudb/PerconaFT/src/tests/run_recover_stress_test.sh
new file mode 100755
index 00000000000..362ea0127c7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_recover_stress_test.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+set -e
+
+test $# -ge 4
+
+bin=$1; shift
+size=$1; shift
+runs=$1; shift
+abortcode=$1; shift
+
+mkdir -p $TOKU_TEST_FILENAME
+$bin -C -n $size -l
+$bin -C -i 0 -n $size -l
+for (( i = 1; i < $runs; i++ ))
+do
+ echo -n "$i: " && date
+ set +e
+ $bin -c -i $i -n $size -l -X novalgrind 2>$TOKU_TEST_FILENAME/error.$i
+ test $? -eq $abortcode || exit 1
+ set -e
+ grep -q 'HAPPY CRASH' $TOKU_TEST_FILENAME/error.$i
+done
diff --git a/storage/tokudb/PerconaFT/src/tests/run_recover_test.sh b/storage/tokudb/PerconaFT/src/tests/run_recover_test.sh
new file mode 100755
index 00000000000..ac974f4b113
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_recover_test.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+set -e
+
+test $# -ge 4
+
+bin=$1; shift
+envdir=$1; shift
+tdbrecover=$1; shift
+tdbdump=$1; shift
+
+echo doing $bin
+$bin --no-shutdown
+rm -rf $envdir/recoverdir
+mkdir $envdir/recoverdir
+cp $envdir/tokudb.directory $envdir/recoverdir/
+cp $envdir/tokudb.environment $envdir/recoverdir/
+cp $envdir/tokudb.rollback $envdir/recoverdir/
+cp $envdir/*.tokulog* $envdir/recoverdir/
+echo doing recovery
+$tdbrecover $envdir/recoverdir $envdir/recoverdir
+echo dump and compare
+$tdbdump -h $envdir foo.db >$envdir/foo.dump
+$tdbdump -h $envdir/recoverdir foo.db >$envdir/recoverdir/foo.dump
+diff -q $envdir/foo.dump $envdir/recoverdir/foo.dump
diff --git a/storage/tokudb/PerconaFT/src/tests/run_recovery_fileops_unit.sh b/storage/tokudb/PerconaFT/src/tests/run_recovery_fileops_unit.sh
new file mode 100755
index 00000000000..0ac623a2d1f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_recovery_fileops_unit.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+set -e
+test $# -ge 3
+
+bin=$1; shift
+errorfile=$1; shift
+abortcode=$1; shift
+
+set +e
+$bin -X novalgrind -c $@ 2> $errorfile
+test $? -eq $abortcode || { cat $errorfile; echo Error: no crash in $errorfile; exit 1; }
+set -e
+grep -q 'HAPPY CRASH' $errorfile || { cat $errorfile; echo Error: incorrect crash in $errorfile; exit 1; }
+rm -f $errorfile
+exec $bin -r $@
diff --git a/storage/tokudb/PerconaFT/src/tests/run_stress_test.py b/storage/tokudb/PerconaFT/src/tests/run_stress_test.py
new file mode 100755
index 00000000000..554c683a439
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_stress_test.py
@@ -0,0 +1,34 @@
+#!/usr/local/bin/python2.6
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+import sys
+import os
+import optparse
+
+# options
+parser = optparse.OptionParser()
+parser.add_option('--test', dest='test', type='string', default=None, help="name of stress test to run")
+parser.add_option('--iterations', dest='iterations', type='int', default=1, help="Number of test iterations (default = 1)")
+parser.add_option('--verbose', dest='verbose', action="store_true", default=False, help="Verbose printing (default = FALSE)")
+options, remainder = parser.parse_args()
+
+def run_test():
+ cmd = options.test
+ if ( options.verbose ): cmd += ' -v'
+ for i in range(options.iterations):
+ os.system(cmd + ' -i %d' % (i))
+
+
+def main(argv):
+ run_test()
+ return 0
+
+if __name__ == '__main__':
+ usage = sys.modules["__main__"].__doc__
+ parser.set_usage(usage)
+ unused_flags, new_argv = parser.parse_args(args=sys.argv[1:], values=options)
+ sys.exit(main([sys.argv[0]] + new_argv))
+
diff --git a/storage/tokudb/PerconaFT/src/tests/run_test_thread_stack.sh b/storage/tokudb/PerconaFT/src/tests/run_test_thread_stack.sh
new file mode 100755
index 00000000000..034dbacac6f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/run_test_thread_stack.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+#
+# This file is part of PerconaFT.
+# Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+#
+
+if [[ $# -ne 1 ]]; then exit 1; fi
+
+bin=$1; shift
+
+set -e
+
+$bin -a -thread_stack 16384
+$bin -a -thread_stack 16384 -resume
diff --git a/storage/tokudb/PerconaFT/src/tests/seqinsert.cc b/storage/tokudb/PerconaFT/src/tests/seqinsert.cc
new file mode 100644
index 00000000000..ccb604d699b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/seqinsert.cc
@@ -0,0 +1,112 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <toku_portability.h>
+#include <toku_os.h>
+#include <memory.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+DB_TXN * const null_txn = nullptr;
+
+const size_t nodesize = 128 << 10;
+const size_t keysize = 8;
+const size_t valsize = 92;
+const size_t rowsize = keysize + valsize;
+const int max_degree = 16;
+const size_t numleaves = max_degree * 3; // want height 2, this should be good enough
+const size_t numrows = (numleaves * nodesize + rowsize) / rowsize;
+
+static void test_seqinsert(bool asc) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->set_pagesize(db, nodesize);
+ CKERR(r);
+ r = db->open(db, null_txn, "seqinsert", NULL, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+
+ char v[valsize];
+ ZERO_ARRAY(v);
+ uint64_t k;
+ DBT key, val;
+ dbt_init(&key, &k, sizeof k);
+ dbt_init(&val, v, valsize);
+ for (size_t i = 0; i < numrows; ++i) {
+ k = toku_htod64(numrows + (asc ? i : -i));
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ }
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main(int argc, char * const argv[]) {
+ default_parse_args(argc, argv);
+
+ test_seqinsert(true);
+ test_seqinsert(false);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/shutdown-3344.cc b/storage/tokudb/PerconaFT/src/tests/shutdown-3344.cc
new file mode 100644
index 00000000000..86eb4ccae84
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/shutdown-3344.cc
@@ -0,0 +1,232 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// measure the cost of closing db's with a full cache table
+
+// create db 0 with txn 0
+// create db's 1..N-1 with auto txn1
+// fill the cache table with blocks for db 0
+// close db 1..N-1 (these should be fast)
+// close db 0
+// abort txn 0
+
+#include "test.h"
+#include <toku_byteswap.h>
+
+static long htonl64(long x) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return bswap_64(x);
+#else
+#error
+#endif
+}
+
+static inline float tdiff (struct timeval *a, struct timeval *b) {
+ return (a->tv_sec - b->tv_sec) +1e-6*(a->tv_usec - b->tv_usec);
+}
+
+static void
+insert_row(DB_ENV *env UU(), DB_TXN *txn, DB *db, uint64_t rowi) {
+ int r;
+
+ // generate the key
+ char key_buffer[8];
+ uint64_t k = htonl64(rowi);
+ memcpy(key_buffer, &k, sizeof k);
+
+ // generate the val
+ char val_buffer[1024];
+ memset(val_buffer, 0, sizeof val_buffer);
+
+ DBT key = { .data = key_buffer, .size = sizeof key_buffer };
+ DBT value = { .data = val_buffer, .size = sizeof val_buffer };
+ //uint32_t put_flags = 0 | (txn ? (DB_PRELOCKED_FILE_READ | DB_PRELOCKED_WRITE) : 0);
+ uint32_t put_flags = 0;
+ r = db->put(db, txn, &key, &value, put_flags); assert_zero(r);
+}
+
+static void
+populate(DB_ENV *env, DB_TXN *txn, DB *db, uint64_t nrows) {
+ int r;
+ struct timeval tstart;
+ r = gettimeofday(&tstart, NULL); assert_zero(r);
+ struct timeval tlast = tstart;
+
+ for (uint64_t rowi = 0; rowi < nrows; rowi++) {
+ insert_row(env, txn, db, rowi);
+
+ // maybe report performance
+ uint64_t rows_per_report = 100000;
+ if (((rowi + 1) % rows_per_report) == 0) {
+ struct timeval tnow;
+ r = gettimeofday(&tnow, NULL); assert_zero(r);
+ float last_time = tdiff(&tnow, &tlast);
+ float total_time = tdiff(&tnow, &tstart);
+ if (verbose) {
+ fprintf(stderr, "%" PRIu64 " %.3f %.0f/s %.0f/s\n", rowi + 1, last_time, rows_per_report/last_time, rowi/total_time); fflush(stderr);
+ }
+ tlast = tnow;
+ }
+ }
+}
+
+static void
+run_test(DB_ENV *env, int ndbs, int do_txn, uint32_t pagesize, uint64_t nrows) {
+ int r;
+
+ DB *dbs[ndbs];
+ for (int i = 0; i < ndbs; i++) {
+ DB *db = NULL;
+ if (verbose) {
+ time_t now = time(0); fprintf(stderr, "%.24s creating %d\n", ctime(&now), i);
+ }
+ r = db_create(&db, env, 0); assert_zero(r);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert_zero(r);
+ }
+ DB_TXN *txn1 = NULL;
+ if (do_txn) {
+ r = env->txn_begin(env, NULL, &txn1, 0); assert_zero(r);
+ }
+ char db_filename[32]; sprintf(db_filename, "test%d", i);
+ r = db->open(db, txn1, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+ if (do_txn) {
+ r = txn1->commit(txn1, 0); assert_zero(r);
+ }
+ dbs[i] = db;
+ }
+
+ if (verbose) {
+ time_t now = time(0); fprintf(stderr, "%.24s populating\n", ctime(&now));
+ }
+
+ DB_TXN *txn0 = NULL;
+ if (do_txn) {
+ r = env->txn_begin(env, NULL, &txn0, 0); assert_zero(r);
+ }
+
+ populate(env, txn0, dbs[ndbs-1], nrows);
+
+ if (do_txn) {
+ if (verbose) {
+ time_t now = time(0); fprintf(stderr, "%.24s commit txn0\n", ctime(&now));
+ }
+ r = txn0->commit(txn0, 0); assert_zero(r);
+ }
+
+ for (int i = 0; i < ndbs; i++) {
+ DB *db = dbs[i];
+ if (verbose) {
+ time_t now = time(0); fprintf(stderr, "%.24s closing %d\n", ctime(&now), i);
+ }
+ r = db->close(db, 0); assert_zero(r);
+ }
+
+ if (verbose) {
+ time_t now = time(0); fprintf(stderr, "%.24s done\n", ctime(&now));
+ }
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ const char *env_dir = "dir.shutdown.ca";
+ int ndbs = 500;
+ int do_txn = 1;
+ uint32_t pagesize = 1024;
+ uint64_t cachesize = 1000000000;
+ uint64_t nrows = 50000;
+
+ for (int i = 1; i < argc ; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ if (verbose > 0) verbose--;
+ continue;
+ }
+ if (strcmp(arg, "--txn") == 0 && i+1 < argc) {
+ do_txn = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--pagesize") == 0 && i+1 < argc) {
+ pagesize = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--cachesize") == 0 && i+1 < argc) {
+ cachesize = atol(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--rows") == 0 && i+1 < argc) {
+ nrows = atol(argv[++i]);
+ continue;
+ }
+
+ assert(0);
+ }
+
+ // create clean env dir
+ char rm_cmd[strlen(env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", env_dir);
+ int r;
+ r = system(rm_cmd); assert_zero(r);
+ r = toku_os_mkdir(env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = env->set_cachesize(env, cachesize / gig, cachesize % gig, 1); assert_zero(r);
+ }
+ int env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ if (!do_txn)
+ env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
+ r = env->open(env, env_dir, env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ run_test(env, ndbs, do_txn, pagesize, nrows);
+
+ if (verbose) fprintf(stderr, "closing env\n");
+ r = env->close(env, 0); assert_zero(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/simple.cc b/storage/tokudb/PerconaFT/src/tests/simple.cc
new file mode 100644
index 00000000000..07c493b7ee5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/simple.cc
@@ -0,0 +1,89 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Purpose of this test is to verify that a failed assert will
+ * cause a panic, which should be visible via engine status.
+ * This is a manual test, should not be checked in to repository.
+ * The panic must be manually induced in the debugger.
+ */
+
+
+#include "test.h"
+#include <db.h>
+
+static DB_ENV *env;
+
+#define FLAGS_NOLOG DB_INIT_LOCK|DB_INIT_MPOOL|DB_CREATE|DB_PRIVATE
+#define FLAGS_LOG FLAGS_NOLOG|DB_INIT_TXN|DB_INIT_LOG
+
+static int mode = S_IRWXU+S_IRWXG+S_IRWXO;
+
+static void test_shutdown(void);
+
+static void
+test_shutdown(void) {
+ int r;
+ r=env->close(env, 0); CKERR(r);
+ env = NULL;
+}
+
+static void
+setup (uint32_t flags) {
+ int r;
+ if (env)
+ test_shutdown();
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r=db_env_create(&env, 0);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, flags, mode);
+ CKERR(r);
+}
+
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup(FLAGS_LOG);
+ env->txn_checkpoint(env, 0, 0, 0);
+ print_engine_status(env);
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/stat64-create-modify-times.cc b/storage/tokudb/PerconaFT/src/tests/stat64-create-modify-times.cc
new file mode 100644
index 00000000000..6826499e3e0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/stat64-create-modify-times.cc
@@ -0,0 +1,129 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the tree create time is retuned in db->stat64
+
+#include "test.h"
+
+#include <db.h>
+#include <sys/stat.h>
+
+static void
+test_stat64_create_time (uint64_t n) {
+ if (verbose) printf("%s:%u\n", __FUNCTION__, __LINE__);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); CKERR(r);
+
+ r = env->set_cachesize(env, 0, 20*1000000, 1);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0); CKERR(r);
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+
+ r = db->set_pagesize(db, 4096); assert(r == 0);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ // initial stats
+ DB_BTREE_STAT64 s0;
+ r = db->stat64(db, txn, &s0); assert(r == 0);
+ printf("initial stat create %" PRIu64 "\n", s0.bt_create_time_sec);
+ assert(s0.bt_create_time_sec != 0);
+ assert(s0.bt_modify_time_sec == s0.bt_create_time_sec);
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ // stats after create is committed
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DB_BTREE_STAT64 s1;
+ r = db->stat64(db, txn, &s1); assert(r == 0);
+ assert(s1.bt_create_time_sec == s0.bt_create_time_sec);
+ assert(s1.bt_modify_time_sec == s0.bt_modify_time_sec);
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ // stats after checkpoint
+ sleep(10);
+ r = env->txn_checkpoint(env, 0, 0, 0); assert(r == 0);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DB_BTREE_STAT64 s2;
+ r = db->stat64(db, txn, &s2); assert(r == 0);
+ assert(s2.bt_create_time_sec == s1.bt_create_time_sec);
+ assert(s2.bt_modify_time_sec > s1.bt_modify_time_sec);
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ // stats after insertion
+ DB_BTREE_STAT64 s3;
+ assert(n > 0);
+ for (uint64_t i = 0; i < n; i++) {
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBT key, val;
+ r = db->put(db, txn, dbt_init(&key, &i, sizeof i), dbt_init(&val, &i, sizeof i), 0);
+ r = db->stat64(db, txn, &s3); assert(r == 0);
+ assert(s3.bt_create_time_sec == s2.bt_create_time_sec);
+ assert(s3.bt_modify_time_sec == s2.bt_modify_time_sec);
+ r = txn->commit(txn, 0); assert(r == 0);
+ }
+
+ // stats after checkpoint
+ sleep(10);
+ r = env->txn_checkpoint(env, 0, 0, 0); assert(r == 0);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DB_BTREE_STAT64 s4;
+ r = db->stat64(db, txn, &s4); assert(r == 0);
+ assert(s4.bt_create_time_sec == s3.bt_create_time_sec);
+ assert(s4.bt_modify_time_sec > s3.bt_modify_time_sec);
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_stat64_create_time(1);
+ test_stat64_create_time(1000);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/stat64-null-txn.cc b/storage/tokudb/PerconaFT/src/tests/stat64-null-txn.cc
new file mode 100644
index 00000000000..492df9aba8a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/stat64-null-txn.cc
@@ -0,0 +1,173 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test the stat64 function on flat databases
+
+#include "test.h"
+
+#include <db.h>
+#include <sys/stat.h>
+
+static void
+test_stat64 (unsigned int N) {
+ if (verbose) printf("%s:%d\n", __FUNCTION__, __LINE__);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *txn;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+
+ r = env->set_cachesize(env, 0, 20*1000000, 1);
+ /* Open the environment without transactions. */
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_MPOOL|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+
+ {
+ /* Don't begin a transaction, just set it to null.
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ */
+ txn = NULL;
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ /* r=txn->commit(txn, 0); assert(r==0); */
+ }
+
+ /* No transactions.
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ */
+
+ // insert sequential keys into the databases
+
+ unsigned int i;
+ uint64_t dsize=0;
+ for (i=0; i<N; i++) {
+ if (verbose>1 && i % (1<<14) == 0) {
+ printf("%s(total=%u) inserted %u so far\n", __FILE__, N, i);
+ fflush(stdout);
+ }
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%8d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ DBT key, val;
+ r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&val, there, strlen(there)+1),
+ 0);
+ dsize += strlen(hello)+1 + strlen(there)+1;
+ CKERR(r);
+ }
+ /* r=txn->commit(txn, 0); CKERR(r); */
+
+ // get and verify stats, should be treated as estimates
+ /* r=env->txn_begin(env, 0, &txn, 0); CKERR(r); */
+ {
+ DB_BTREE_STAT64 s;
+ r=db->stat64(db, txn, &s); CKERR(r);
+ if (verbose) {
+ char cmd[sizeof("ls -l ") + TOKU_PATH_MAX];
+ snprintf(cmd, sizeof(cmd), "ls -l %s", TOKU_TEST_FILENAME);
+ r = system(cmd);
+ CKERR(r);
+ printf("nkeys=%" PRIu64 "\nndata=%" PRIu64 "\ndsize=%" PRIu64 "\n",
+ s.bt_nkeys, s.bt_ndata, s.bt_dsize);
+ printf("fsize=%" PRIu64 "\n", s.bt_fsize);
+ printf("expected dsize=%" PRIu64 "\n", dsize);
+ }
+ assert(0 < s.bt_nkeys && s.bt_nkeys <= N);
+ assert(s.bt_ndata == s.bt_nkeys);
+ assert(0 < s.bt_dsize && s.bt_dsize <= dsize);
+ // cannot reliably test bt_fsize, because it
+ // just measures size of file on disk
+ // assert(s.bt_fsize > N);
+ }
+ /* r=txn->commit(txn, 0); CKERR(r); */
+
+ // get the last row, this forces the root estimates to be updated
+ {
+ /* r = env->txn_begin(env, NULL, &txn, 0); CKERR(r); */
+ DBC *c = NULL;
+ r = db->cursor(db, txn, &c, 0); CKERR(r);
+ DBT key; dbt_init(&key, NULL, 0);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = c->c_get(c, &key, &val, DB_LAST);
+ CKERR(r);
+ r = c->c_close(c); CKERR(r);
+ /* r = txn->commit(txn, 0); CKERR(r); */
+ }
+
+ // get and verify stats
+ /* r=env->txn_begin(env, 0, &txn, 0); CKERR(r); */
+ {
+ DB_BTREE_STAT64 s;
+ r=db->stat64(db, txn, &s); CKERR(r);
+ if (verbose) {
+ char cmd[sizeof("ls -l ") + TOKU_PATH_MAX];
+ snprintf(cmd, sizeof(cmd), "ls -l %s", TOKU_TEST_FILENAME);
+ r = system(cmd);
+ CKERR(r);
+ printf("nkeys=%" PRIu64 "\nndata=%" PRIu64 "\ndsize=%" PRIu64 "\n",
+ s.bt_nkeys, s.bt_ndata, s.bt_dsize);
+ printf("fsize=%" PRIu64 "\n", s.bt_fsize);
+ printf("expected dsize=%" PRIu64 "\n", dsize);
+ }
+ assert(0 < s.bt_nkeys && s.bt_nkeys <= N);
+ assert(s.bt_ndata == s.bt_nkeys);
+ assert(0 < s.bt_dsize && s.bt_dsize <= dsize);
+ // cannot reliably test bt_fsize, because it
+ // just measures size of file on disk
+ //assert(s.bt_fsize > N);
+ }
+ /* r=txn->commit(txn, 0); CKERR(r); */
+
+ r=db->close(db, 0); CKERR(r);
+
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char *const argv[])
+{
+ parse_args(argc, argv);
+ test_stat64(40000);
+ test_stat64(400000);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/stat64-root-changes.cc b/storage/tokudb/PerconaFT/src/tests/stat64-root-changes.cc
new file mode 100644
index 00000000000..0c70b0669ad
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/stat64-root-changes.cc
@@ -0,0 +1,249 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify stats after a new row inserted into the root
+// verify stats after a row overwrite in the root
+// verify stats after a row deletion in the root
+// verify stats after an update callback inserts row
+// verify stats after an update callback overwrites row
+// verify ststs after an update callback deletes row
+
+#include <db.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static int
+my_update_callback(DB *db UU(), const DBT *key UU(), const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra) {
+ if (old_val != NULL && old_val->size == 42) // special code for delete
+ set_val(NULL, set_extra);
+ else
+ set_val(extra, set_extra);
+ return 0;
+}
+
+static void
+run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_redzone(env, 0); CKERR(r);
+ env->set_update(env, my_update_callback);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0); CKERR(r);
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // verify that stats include a new row inserted into the root
+ {
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ int key = 1; char val = 1;
+ DBT k,v;
+ r = db->put(db, txn, dbt_init(&k, &key, sizeof key), dbt_init(&v, &val, sizeof val), 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ DB_BTREE_STAT64 s;
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys == 1 && s.bt_dsize == sizeof key + sizeof val);
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys == 1 && s.bt_dsize == sizeof key + sizeof val);
+ }
+
+ // verify that stats are updated by row overwrite in the root
+ {
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ int key = 1; int val = 2;
+ DBT k,v;
+ r = db->put(db, txn, dbt_init(&k, &key, sizeof key), dbt_init(&v, &val, sizeof val), 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ DB_BTREE_STAT64 s;
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys == 1 && s.bt_dsize == sizeof key + sizeof val);
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys == 1 && s.bt_dsize == sizeof key + sizeof val);
+ }
+
+ // verify that stats are updated by row deletion in the root
+ {
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ int key = 1;
+ DBT k;
+ r = db->del(db, txn, dbt_init(&k, &key, sizeof key), 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ DB_BTREE_STAT64 s;
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys <= 1 && s.bt_dsize == 0); // since garbage collection may not occur, the key count may not be updated
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ // garbage collection has happened in db->close, so
+ // the number of keys should be 0
+ assert(s.bt_nkeys == 0 && s.bt_dsize == 0);
+ }
+
+ // verify update of non-existing key inserts a row
+ //
+ //
+ // NOTE: #5744 was caught by this test below.
+ //
+ //
+ {
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ int key = 1; char val = 1;
+ DBT k = { .data = &key, .size = sizeof key };
+ DBT e = { .data = &val, .size = sizeof val };
+ r = db->update(db, txn, &k, &e, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ DB_BTREE_STAT64 s;
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys == 1 && s.bt_dsize == sizeof key + sizeof val);
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys == 1 && s.bt_dsize == sizeof key + sizeof val);
+ }
+
+ // verify update callback overwrites the row
+ {
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ int key = 1; int val = 2;
+ DBT k = { .data = &key, .size = sizeof key };
+ DBT e = { .data = &val, .size = sizeof val };
+ r = db->update(db, txn, &k, &e, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ DB_BTREE_STAT64 s;
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys == 1 && s.bt_dsize == sizeof key + sizeof val);
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys == 1 && s.bt_dsize == sizeof key + sizeof val);
+ }
+
+ // verify update callback deletes the row
+ {
+ // insert a new row
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ int key = 1; char val[42]; memset(val, 0, sizeof val);
+ DBT k = { .data = &key, .size = sizeof key };
+ DBT e = { .data = &val, .size = sizeof val };
+ r = db->update(db, txn, &k, &e, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ DB_BTREE_STAT64 s;
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys <= 2 && s.bt_dsize == sizeof key + sizeof val);
+
+ // update it again, this should delete the row
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->update(db, txn, &k, &e, 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys <= 2 && s.bt_dsize == 0); // since garbage collection may not occur, the key count may not be updated
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->stat64(db, NULL, &s); CKERR(r);
+ assert(s.bt_nkeys <= 2 && s.bt_dsize == 0);
+ }
+
+ r = db->close(db, 0); CKERR(r);
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc , char * const argv[]) {
+ parse_args(argc, argv);
+ run_test();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/stat64.cc b/storage/tokudb/PerconaFT/src/tests/stat64.cc
new file mode 100644
index 00000000000..40773e52883
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/stat64.cc
@@ -0,0 +1,168 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test the stat64 function on flat databases
+
+#include "test.h"
+
+#include <db.h>
+#include <sys/stat.h>
+
+static void
+test_stat64 (unsigned int N) {
+ if (verbose) printf("%s:%d\n", __FUNCTION__, __LINE__);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *txn;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+
+ r = env->set_cachesize(env, 0, 20*1000000, 1);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+
+ {
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+ }
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ // insert sequential keys into the databases
+ unsigned int i;
+ uint64_t dsize=0;
+ for (i=0; i<N; i++) {
+ if (verbose>1 && i % (1<<14) == 0) {
+ printf("%s(total=%u) inserted %u so far\n", __FILE__, N, i);
+ fflush(stdout);
+ }
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%8d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ DBT key, val;
+ r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&val, there, strlen(there)+1),
+ 0);
+ dsize += strlen(hello)+1 + strlen(there)+1;
+ CKERR(r);
+ }
+ r=txn->commit(txn, 0); CKERR(r);
+
+ // get and verify stats, should be treated as estimates
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ {
+ DB_BTREE_STAT64 s;
+ r=db->stat64(db, txn, &s); CKERR(r);
+ if (verbose) {
+ char cmd[sizeof("ls -l ") + TOKU_PATH_MAX];
+ snprintf(cmd, sizeof(cmd), "ls -l %s", TOKU_TEST_FILENAME);
+ r = system(cmd);
+ CKERR(r);
+ printf("N=%d\n", N);
+ printf("nkeys=%" PRIu64 "\nndata=%" PRIu64 "\ndsize=%" PRIu64 "\n",
+ s.bt_nkeys, s.bt_ndata, s.bt_dsize);
+ printf("fsize=%" PRIu64 "\n", s.bt_fsize);
+ printf("expected dsize=%" PRIu64 "\n", dsize);
+ }
+ assert(0 < s.bt_nkeys && s.bt_nkeys <= N);
+ assert(s.bt_ndata == s.bt_nkeys);
+ assert(0 < s.bt_dsize && s.bt_dsize <= dsize);
+ // cannot reliably test bt_fsize, because it
+ // measures the size of the file on disk.
+ //assert(s.bt_fsize > N);
+ }
+ r=txn->commit(txn, 0); CKERR(r);
+
+ // get the last row, this forces the root estimates to be updated.
+ {
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DBC *c = NULL;
+ r = db->cursor(db, txn, &c, 0); CKERR(r);
+ DBT key; dbt_init(&key, NULL, 0);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = c->c_get(c, &key, &val, DB_LAST);
+ CKERR(r);
+ r = c->c_close(c); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+
+ // get and verify stats
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ {
+ DB_BTREE_STAT64 s;
+ r=db->stat64(db, txn, &s); CKERR(r);
+ if (verbose) {
+ char cmd[sizeof("ls -l ") + TOKU_PATH_MAX];
+ snprintf(cmd, sizeof(cmd), "ls -l %s", TOKU_TEST_FILENAME);
+ r = system(cmd);
+ CKERR(r);
+ printf("N=%d\n", N);
+ printf("nkeys=%" PRIu64 "\nndata=%" PRIu64 "\ndsize=%" PRIu64 "\n",
+ s.bt_nkeys, s.bt_ndata, s.bt_dsize);
+ printf("fsize=%" PRIu64 "\n", s.bt_fsize);
+ printf("expected dsize=%" PRIu64 "\n", dsize);
+ }
+ assert(0 < s.bt_nkeys && s.bt_nkeys <= N);
+ assert(s.bt_ndata == s.bt_nkeys);
+ assert(0 < s.bt_dsize && s.bt_dsize <= dsize);
+ // cannot reliably test bt_fsize, because it
+ // measures the size of the file on disk.
+ //assert(s.bt_fsize > N);
+ }
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=db->close(db, 0); CKERR(r);
+
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char *const argv[])
+{
+ parse_args(argc, argv);
+ test_stat64(40000);
+ test_stat64(400000);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/stress-gc.cc b/storage/tokudb/PerconaFT/src/tests/stress-gc.cc
new file mode 100644
index 00000000000..850d5b5caa2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/stress-gc.cc
@@ -0,0 +1,115 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Test that isolation works right for subtransactions.
+// In particular, check to see what happens if a subtransaction has different isolation level from its parent.
+
+#include "test.h"
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int useseed;
+
+ {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ useseed = tv.tv_sec+tv.tv_usec*997; // magic: 997 is a prime, and a million (microseconds/second) times 997 is still 32 bits.
+ }
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ db_env_set_mvcc_garbage_collection_verification(1);
+ int max_txns = 400;
+ int num_runs = 2000;
+ DB_TXN* txns[max_txns];
+ memset(txns, 0, sizeof(txns));
+ int num_txns = 0;
+ int i;
+
+ if (verbose) printf("seed=%d\n", useseed);
+ srandom(useseed);
+
+ for (i = 0; i < num_runs; i++) {
+ int rand_num = random()%max_txns;
+ /*
+ if (i%50 == 0) {
+ printf("rand_num %d\n", rand_num);
+ printf("num_txns %d\n", num_txns);
+ printf("iteration %d\n", i);
+ }
+ */
+ if (rand_num >= num_txns) {
+ // add a txn
+ assert(txns[num_txns] == NULL);
+ // 7 out of 8 times, it is snapshot, otherwise, serializable
+ int is_snapshot = (random() % 8 != 0);
+ r = env->txn_begin(env, NULL, &txns[num_txns], is_snapshot ? DB_TXN_SNAPSHOT : 0);
+ CKERR(r);
+ num_txns++;
+ }
+ else {
+ // commit the txn
+ r = txns[rand_num]->commit(txns[rand_num], 0);
+ CKERR(r);
+ int j;
+ for (j = rand_num; j < num_txns-1; j++) {
+ txns[j] = txns[j+1];
+ }
+ txns[num_txns-1] = NULL;
+ num_txns--;
+ }
+ }
+
+ for (i = 0; i < num_txns; i++) {
+ r = txns[i]->commit(txns[i], 0);
+ CKERR(r);
+ }
+
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/stress-gc2.cc b/storage/tokudb/PerconaFT/src/tests/stress-gc2.cc
new file mode 100644
index 00000000000..01339f0dae3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/stress-gc2.cc
@@ -0,0 +1,81 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+// The intent of this test is to measure create and abort transactions
+// with garbage collection verification on
+
+static int random_sleep(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ usleep(random()%2000);
+ return 0;
+}
+
+
+static void
+stress_table(DB_ENV* env, DB** dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ myargs[i].operation = random_sleep;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int UU(argc), char *const UU(argv[])) {
+ struct cli_args args = get_default_args_for_perf();
+ db_env_set_mvcc_garbage_collection_verification(1);
+ args.num_seconds = 60;
+ args.num_ptquery_threads = 12;
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/stress-test.cc b/storage/tokudb/PerconaFT/src/tests/stress-test.cc
new file mode 100644
index 00000000000..9d21f6959f9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/stress-test.cc
@@ -0,0 +1,264 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+enum state {
+ UNTOUCHED = 0,
+ INSERTED,
+ DELETED
+};
+struct pair {
+ DBT key, val;
+ enum state state;
+};
+
+#define NKEYS (1<<20)
+#define NDELS (1<<17)
+
+int keys[NKEYS];
+struct pair pairs[NKEYS];
+struct pair sorted[NKEYS];
+int dels[NDELS];
+
+char some_data[200] = ("abetefocebbrk3894d,h"
+ "tebe73t90htb349i83d4"
+ "h3498bk4onhaosnetkb0"
+ "bk934bkgpbk0,8kh4c.r"
+ "bk9,438k4bkr,09k8hkb"
+ "bk9,gr,gkhb,k9,.bkg,"
+ "b4kg4,39k,3k890,.bkr"
+ "bugk349kc,b.rk,.0k8,"
+ "bkreb,0k8.p,k,r,bkhr"
+ "kb.rpgxbeu0xcehu te");
+
+static int
+pair_cmp(const void *a, const void *b)
+{
+ const struct pair *CAST_FROM_VOIDP(p1, a);
+ const struct pair *CAST_FROM_VOIDP(p2, b);
+ if (p1->key.size < p2->key.size) {
+ int c = memcmp(p1->key.data, p2->key.data, p1->key.size);
+ if (!c) {
+ return -1;
+ }
+ return c;
+ } else if (p1->key.size > p2->key.size) {
+ int c = memcmp(p1->key.data, p2->key.data, p2->key.size);
+ if (!c) {
+ return 1;
+ }
+ return c;
+ } else {
+ return memcmp(p1->key.data, p2->key.data, p1->key.size);
+ }
+}
+
+static void
+gen_data(void)
+{
+ srandom(0);
+ for (int i = 0; i < NKEYS; ++i) {
+ keys[i] = htonl(i);
+ }
+ for (int e = NKEYS-1; e > 0; --e) {
+ int r = random() % e;
+ int t = keys[r];
+ keys[r] = keys[e];
+ keys[e] = t;
+ }
+ for (int i = 0; i < NKEYS; ++i) {
+ int vallen = random() % 150;
+ int idx = random() % (200 - vallen);
+ dbt_init(&pairs[i].key, &keys[i], sizeof keys[i]);
+ dbt_init(&pairs[i].val, &some_data[idx], vallen);
+ pairs[i].state = UNTOUCHED;
+ }
+
+ for (int i = 0; i < NDELS; ) {
+ int idx = random() % NKEYS;
+ if (pairs[idx].state != DELETED) {
+ dels[i++] = idx;
+ pairs[idx].state = DELETED;
+ }
+ }
+ for (int i = 0; i < NDELS; ++i) {
+ pairs[dels[i]].state = UNTOUCHED;
+ }
+}
+
+static void
+run_test(DB *db)
+{
+ DB_TXN * const null_txn = 0;
+ int p = 0, d = 0;
+ for (int cursz = NKEYS / 10; cursz <= NKEYS; cursz += NKEYS / 10) {
+ // insert a chunk
+ for (; p < cursz; ++p) {
+ // put an element in
+ invariant(pairs[p].state == UNTOUCHED);
+ { int chk_r = db->put(db, null_txn, &pairs[p].key, &pairs[p].val, 0); CKERR(chk_r); }
+ pairs[p].state = INSERTED;
+ // delete everything we can so far, in the given order
+ for (; d < NDELS && dels[d] <= p; ++d) {
+ invariant(pairs[dels[d]].state == INSERTED);
+ { int chk_r = db->del(db, null_txn, &pairs[dels[d]].key, 0); CKERR(chk_r); }
+ pairs[dels[d]].state = DELETED;
+ }
+ }
+
+ // get what the data should be
+ memcpy(sorted, pairs, cursz * (sizeof pairs[0]));
+ qsort(sorted, cursz, sizeof sorted[0], pair_cmp);
+
+ // verify the data
+
+ // with point queries
+ if ((random() % 10) < 5) {
+ for (int i = 0; i < cursz; ++i) {
+ DBT val; dbt_init(&val, NULL, 0);
+ invariant(sorted[i].state != UNTOUCHED);
+ int r = db->get(db, null_txn, &sorted[i].key, &val, 0);
+ if (sorted[i].state == INSERTED) {
+ CKERR(r);
+ assert(val.size == sorted[i].val.size);
+ assert(memcmp(val.data, sorted[i].val.data, val.size) == 0);
+ } else {
+ CKERR2(r, DB_NOTFOUND);
+ }
+ }
+ }
+
+ // with a forward traversal
+ if ((random() % 10) < 5) {
+ DBC *cur;
+ { int chk_r = db->cursor(db, null_txn, &cur, 0); CKERR(chk_r); }
+ DBT ck, cv; dbt_init(&ck, NULL, 0); dbt_init(&cv, NULL, 0);
+ int i, r;
+ r = cur->c_get(cur, &ck, &cv, DB_FIRST);
+ CKERR(r);
+ for (i = 0;
+ r == 0 && i < cursz;
+ r = cur->c_get(cur, &ck, &cv, DB_NEXT), ++i) {
+ invariant(sorted[i].state != UNTOUCHED);
+ while (i < cursz && sorted[i].state == DELETED) {
+ i++;
+ invariant(sorted[i].state != UNTOUCHED);
+ }
+ invariant(i < cursz);
+ assert(ck.size == sorted[i].key.size);
+ assert(memcmp(ck.data, sorted[i].key.data, ck.size) == 0);
+ assert(cv.size == sorted[i].val.size);
+ assert(memcmp(cv.data, sorted[i].val.data, cv.size) == 0);
+ }
+ while (i < cursz && sorted[i].state == DELETED) {
+ i++;
+ invariant(sorted[i].state != UNTOUCHED);
+ }
+ assert(i == cursz);
+ assert(r == DB_NOTFOUND);
+ }
+
+ // with a backward traversal
+ if ((random() % 10) < 5) {
+ DBC *cur;
+ { int chk_r = db->cursor(db, null_txn, &cur, 0); CKERR(chk_r); }
+ DBT ck, cv; dbt_init(&ck, NULL, 0); dbt_init(&cv, NULL, 0);
+ int i, r;
+ r = cur->c_get(cur, &ck, &cv, DB_LAST);
+ CKERR(r);
+ for (i = cursz - 1;
+ r == 0 && i >= 0;
+ r = cur->c_get(cur, &ck, &cv, DB_PREV), --i) {
+ invariant(sorted[i].state != UNTOUCHED);
+ while (i >= 0 && sorted[i].state == DELETED) {
+ i--;
+ invariant(sorted[i].state != UNTOUCHED);
+ }
+ invariant(i >= 0);
+ assert(ck.size == sorted[i].key.size);
+ assert(memcmp(ck.data, sorted[i].key.data, ck.size) == 0);
+ assert(cv.size == sorted[i].val.size);
+ assert(memcmp(cv.data, sorted[i].val.data, cv.size) == 0);
+ }
+ while (i >= 0 && sorted[i].state == DELETED) {
+ i--;
+ invariant(sorted[i].state != UNTOUCHED);
+ }
+ assert(i == -1);
+ assert(r == DB_NOTFOUND);
+ }
+ }
+}
+
+static void
+init_db(DB_ENV **env, DB **db)
+{
+ DB_TXN * const null_txn = 0;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(env, 0); CKERR(chk_r); }
+ (*env)->set_errfile(*env, stderr);
+ { int chk_r = (*env)->open(*env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); CKERR(chk_r); }
+ { int chk_r = db_create(db, *env, 0); CKERR(chk_r); }
+ { int chk_r = (*db)->open(*db, null_txn, "test.stress.ft_handle", "main",
+ DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+}
+
+static void
+destroy_db(DB_ENV *env, DB *db)
+{
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+int
+test_main(int argc, char * const argv[])
+{
+ DB_ENV *env;
+ DB *db;
+
+ parse_args(argc, argv);
+ gen_data();
+ init_db(&env, &db);
+ run_test(db);
+ destroy_db(env, db);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/stress_openclose.h b/storage/tokudb/PerconaFT/src/tests/stress_openclose.h
new file mode 100644
index 00000000000..3f10bfe8aeb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/stress_openclose.h
@@ -0,0 +1,287 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+#include <toku_pthread.h>
+#include "test.h"
+#include "threaded_stress_test_helpers.h"
+#include <portability/toku_atomic.h>
+
+// set this to true for the recovery version of this stress test
+// the way this works is to include this header and set
+// crash_at_end = true;
+static bool stress_openclose_crash_at_end;
+
+//
+// Stress test ft reference counting
+//
+// Three things keep a fractal tree in memory by holding a reference:
+// - open ft handle
+// - live txn that did a write op
+// - checkpoint
+//
+// To stress reference counting, we would like threads which:
+// - take checkpoints at random intervals
+// - update random values, random point queries for auditing
+// * sometimes close handle before commit.
+// - close random dictionaries
+//
+// Here's how we can do it:
+//
+// A bunch of threads randomly choose from N buckets. Each bucket
+// has a DB, an is_open bit, and a lock.
+// - in a single txn, each thread will do some small number of
+// queries or updates on randomb uckets, opening the dbs if
+// they were closed and possibly closing afterwards.
+// - this should stress both open db handles and various txns
+// references dbs simultaneously.
+//
+// and all while this is happening, throw in scanners, updaters,
+// and query threads that all assert the contents of these dbs
+// is correct, even after recovery.
+
+#define verbose_printf(...) \
+ do { \
+ if (verbose) { \
+ printf(__VA_ARGS__); \
+ fflush(stdout); \
+ } \
+ } while (0)
+
+// a bunch of buckets with dbs, a lock, and an is_open bit
+// threads will choose buckets randomly for update, query,
+// and then maybe open/close the bucket's db.
+struct db_bucket {
+ DB_ENV *env;
+ DB *db;
+ bool is_open;
+ toku_mutex_t mutex;
+};
+static struct db_bucket *buckets;
+static int num_buckets;
+
+// each operation can do at most this many operations in one txn
+static int
+choose_random_iteration_count(ARG arg) {
+ const int max_iteration_count = 8;
+ int k = myrandom_r(arg->random_data) % max_iteration_count;
+ return k + 1;
+}
+
+// open the ith db in the array, asserting success
+static void
+open_ith_db(DB_ENV *env, DB **db, int i) {
+ char name[30];
+ memset(name, 0, sizeof(name));
+ get_ith_table_name(name, sizeof(name), i);
+ int r = db_create(db, env, 0);
+ CKERR(r);
+ r = (*db)->open(*db, null_txn, name, NULL, DB_BTREE, 0, 0666);
+ CKERR(r);
+}
+
+// debugging counter to maintain the invariant that open_buckets <= num_buckets
+static int open_buckets;
+
+// choose and lock a random bucket, possibly opening a db
+static struct db_bucket *
+lock_and_maybe_open_some_db(ARG arg) {
+ int k = myrandom_r(arg->random_data) % num_buckets;
+ struct db_bucket *bucket = &buckets[k];
+ toku_mutex_lock(&bucket->mutex);
+ if (!bucket->is_open) {
+ // choose a random DB from 0..k-1 to associate with this bucket
+ // then, mark the bucket as open.
+ int i = myrandom_r(arg->random_data) % num_buckets;
+ open_ith_db(bucket->env, &bucket->db, i);
+ bucket->is_open = true;
+ assert(toku_sync_fetch_and_add(&open_buckets, 1) < num_buckets);
+ verbose_printf("opened db %d in bucket %d\n", i, k);
+ }
+ return bucket;
+}
+
+// release the lock on a bucket, possibly closing its db
+static void
+unlock_and_maybe_close_db(struct db_bucket *bucket, ARG arg) {
+ static const int p = 5;
+ int k = ((unsigned) myrandom_r(arg->random_data)) % 100;
+ // we should close with probability approximately p / 100
+ assert(bucket->is_open);
+ if (k <= p) {
+ DB *db = bucket->db;
+ int r = db->close(db, 0);
+ CKERR(r);
+ bucket->is_open = false;
+ int old_open_buckets = toku_sync_fetch_and_sub(&open_buckets, 1);
+ assert(old_open_buckets > 0);
+ verbose_printf("decided to close a bucket's db before unlocking\n");
+ }
+ toku_mutex_unlock(&bucket->mutex);
+}
+
+// scan some dbs, verifying the correct sum.
+static int
+scan_some_dbs(DB_TXN *txn, ARG arg, void* operation_extra, void *UU(stats_extra)) {
+ int r = 0;
+ verbose_printf("scanning some dbs\n");
+ struct scan_op_extra* CAST_FROM_VOIDP(extra, operation_extra);
+ // scan every db, one by one, and verify that the contents are correct
+ for (int i = 0; r == 0 && run_test && i < arg->cli->num_DBs; i++) {
+ struct db_bucket *bucket = lock_and_maybe_open_some_db(arg);
+ const bool check_sum = true;
+ r = scan_op_and_maybe_check_sum(bucket->db, txn, extra, check_sum);
+ invariant(r == 0 || r == DB_LOCK_NOTGRANTED);
+ unlock_and_maybe_close_db(bucket, arg);
+ }
+ return r;
+}
+
+// update a couple of dbs in some buckets with a txn
+static int
+update_some_dbs(DB_TXN *txn, ARG arg, void *op_extra, void *stats_extra) {
+ int r = 0;
+ verbose_printf("updating some dbs\n");
+ const int iterations = choose_random_iteration_count(arg);
+ for (int i = 0; r == 0 && run_test && i < iterations; i++) {
+ struct db_bucket *bucket = lock_and_maybe_open_some_db(arg);
+ // does an update operation on this bucket's db
+ r = update_op_db(bucket->db, txn, arg, op_extra, stats_extra);
+ invariant(r == 0 || r == DB_LOCK_NOTGRANTED);
+ unlock_and_maybe_close_db(bucket, arg);
+ }
+ return r;
+}
+
+// point query a couple of dbs in some buckets with a txn
+static int
+ptquery_some_dbs(DB_TXN *txn, ARG arg, void *UU(op_extra), void *UU(stats_extra)) {
+ int r = 0;
+ verbose_printf("querying some dbs\n");
+ const int iterations = choose_random_iteration_count(arg);
+ for (int i = 0; r == 0 && run_test && i < iterations; i++) {
+ struct db_bucket *bucket = lock_and_maybe_open_some_db(arg);
+ // does a point query on a random key for this bucket's db
+ const bool check_sum = true;
+ r = ptquery_and_maybe_check_op(bucket->db, txn, arg, check_sum);
+ invariant(r == 0 || r == DB_LOCK_NOTGRANTED);
+ unlock_and_maybe_close_db(bucket, arg);
+ }
+ return r;
+}
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ const int update_threads = cli_args->num_update_threads;
+ const int query_threads = cli_args->num_ptquery_threads;
+ const int total_threads = update_threads + query_threads + 1;
+
+ struct arg myargs[total_threads];
+ for (int i = 0; i < total_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+
+ struct scan_op_extra soe[4];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_some_dbs;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 1; i < 1 + update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_some_dbs;
+ myargs[i].do_prepare = true;
+ }
+ // make the guy that does point queries
+ for (int i = 1 + update_threads; i < total_threads; i++) {
+ myargs[i].operation = ptquery_some_dbs;
+ myargs[i].do_prepare = true;
+ }
+
+ num_buckets = cli_args->num_DBs;
+ open_buckets = num_buckets;
+ // each thread gets access to this array of db buckets, from
+ // which they can choose a random db to either touch or query
+ XMALLOC_N(num_buckets, buckets);
+ for (int i = 0; i < num_buckets; i++) {
+ struct db_bucket bucket = {
+ .env = env,
+ .db = dbp[i],
+ .is_open = true
+ };
+ buckets[i] = bucket;
+ toku_mutex_init(&buckets[i].mutex, NULL);
+ }
+ // run all of the query and update workers. they may randomly open
+ // and close the dbs in each db_bucket to be some random dictionary,
+ // so when they're done we'll have to clean up the mess so this
+ // stress test can exit gracefully expecting db[i] = the ith db
+ //verbose_printf("stressing %d tables using %d update threads, %d query threads\n",
+ // num_buckets, update_threads, query_threads);
+ verbose_printf("stressing %d tables using %d update threads\n",
+ num_buckets, update_threads);
+ // stress_openclose_crash_at_end should be changed to true or false,
+ // depending if this test is for recovery or not.
+ const bool crash_at_end = stress_openclose_crash_at_end;
+ run_workers(myargs, total_threads, cli_args->num_seconds, crash_at_end, cli_args);
+
+ // the stress test is now complete. get ready for shutdown/close.
+ //
+ // make sure that every db in the original array is opened
+ // as it was when it was passed in.
+ for (int i = 0; i < num_buckets; i++) {
+ // close whatever is open
+ if (buckets[i].is_open) {
+ DB *db = buckets[i].db;
+ int r = db->close(db, 0);
+ CKERR(r);
+ }
+ // put the correct db back, then save the pointer
+ // into the dbp array we were given
+ open_ith_db(env, &buckets[i].db, i);
+ dbp[i] = buckets[i].db;
+ }
+
+ toku_free(buckets);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test-5138.cc b/storage/tokudb/PerconaFT/src/tests/test-5138.cc
new file mode 100644
index 00000000000..4a55da02b8c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test-5138.cc
@@ -0,0 +1,87 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that with full optimizations including the "last IPO pass" and
+// static linking doesn't break lzma
+
+#include "test.h"
+
+int test_main(int argc, char * const argv[]) {
+ int r;
+ parse_args(argc, argv);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB *db;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->set_compression_method(db, TOKU_LZMA_METHOD);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+
+ DBT key, val;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &i, sizeof(i));
+ for (i = 0; i < 1000; ++i) {
+ r = db->put(db, txn, keyp, valp, 0);
+ CKERR(r);
+ }
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = db->close(db, 0);
+ CKERR(r);
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test-nested-xopen-eclose.cc b/storage/tokudb/PerconaFT/src/tests/test-nested-xopen-eclose.cc
new file mode 100644
index 00000000000..ee6b80e16c4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test-nested-xopen-eclose.cc
@@ -0,0 +1,142 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the env close aborts open txns
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ DB_TXN *txnb;
+ r = env->txn_begin(env, txn, &txnb, 0); CKERR(r);
+
+ r = env->close(env, 0);
+ assert(r == EINVAL);
+
+#if 0
+ r = txn->abort(txn); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+#endif
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char *const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char *const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test-prepare.cc b/storage/tokudb/PerconaFT/src/tests/test-prepare.cc
new file mode 100644
index 00000000000..2f04afa21c8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test-prepare.cc
@@ -0,0 +1,139 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/wait.h>
+
+static void clean_env (const char *envdir) {
+ const int len = strlen(envdir)+100;
+ char cmd[len];
+ snprintf(cmd, len, "rm -rf %s", envdir);
+ int r = system(cmd);
+ CKERR(r);
+ CKERR(toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO));
+}
+
+static void setup_env (DB_ENV **envp, const char *envdir) {
+ { int chk_r = db_env_create(envp, 0); CKERR(chk_r); }
+ (*envp)->set_errfile(*envp, stderr);
+ { int chk_r = (*envp)->set_redzone(*envp, 0); CKERR(chk_r); }
+ { int chk_r = (*envp)->open(*envp, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void setup_env_and_prepare (DB_ENV **envp, const char *envdir, bool commit) {
+ DB *db;
+ DB_TXN *txn;
+ clean_env(envdir);
+ setup_env(envp, envdir);
+ CKERR(db_create(&db, *envp, 0));
+ CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO));
+ CKERR((*envp)->txn_begin(*envp, 0, &txn, 0));
+ DBT key;
+ dbt_init(&key, "foo", 4);
+ CKERR(db->put(db, txn, &key, &key, 0));
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ uint8_t gid[DB_GID_SIZE];
+ memset(gid, 0, DB_GID_SIZE);
+ gid[0]=42;
+ CKERR(txn->prepare(txn, gid, 0));
+ if (commit)
+ CKERR(txn->commit(txn, 0));
+}
+
+static void test1 (void) {
+ pid_t pid;
+ bool do_fork = true;
+ if (!do_fork || 0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env_and_prepare(&env, TOKU_TEST_FILENAME, false);
+ {
+ DB_PREPLIST l[1];
+ long count=-1;
+ CKERR(env->txn_recover(env, l, 1, &count, DB_FIRST));
+ printf("%s:%d count=%ld\n", __FILE__, __LINE__, count);
+ assert(count==1);
+ assert(l[0].gid[0]==42);
+ }
+ exit(0);
+ }
+ int status;
+ if (do_fork) {
+ pid_t pid2 = wait(&status);
+ assert(pid2==pid);
+ }
+
+ DB_ENV *env2;
+ char envdir2[TOKU_PATH_MAX+1];
+ setup_env_and_prepare(&env2, toku_path_join(envdir2, 2, TOKU_TEST_FILENAME, "envdir2"), true);
+
+ // Now we can look at env2 in the debugger to see if we managed to make it the same
+
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+
+ {
+ DB_PREPLIST l[1];
+ long count=-1;
+ int r = env->txn_recover(env, l, 1, &count, DB_FIRST);
+ printf("r=%d count=%ld\n", r, count);
+ assert(count==1);
+ assert(l[0].gid[0]==42);
+ for (int i=1; i<DB_GID_SIZE; i++) {
+ assert(l[0].gid[i]==0);
+ }
+ { int chk_r = l->txn->commit(l->txn, 0); CKERR(chk_r); }
+ }
+ { int chk_r = env2->close(env2, 0); CKERR(chk_r); }
+ { int chk_r = env ->close(env, 0); CKERR(chk_r); }
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+ // first test: open an environment, a db, a txn, and do a prepare. Then do txn_prepare (without even closing the environment).
+ test1();
+
+
+ // second test: poen environment, a db, a txn, prepare, close the environment. Then reopen and do txn_prepare.
+
+ // third test: make sure there is an fsync on txn_prepare, but not on the following commit.
+
+
+ // Then close the environment Find out what BDB does when ask for the txn prepares.
+ // Other tests: read prepared txns, 1 at a time. Then close it and read them again.
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test-prepare2.cc b/storage/tokudb/PerconaFT/src/tests/test-prepare2.cc
new file mode 100644
index 00000000000..73d82aad93c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test-prepare2.cc
@@ -0,0 +1,161 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/wait.h>
+
+// Verify that if tokudb crashes during recovery, then the prepared transactions are still prepared.
+
+static void clean_env (const char *envdir) {
+ const int len = strlen(envdir)+100;
+ char cmd[len];
+ snprintf(cmd, len, "rm -rf %s", envdir);
+ int r = system(cmd);
+ CKERR(r);
+ CKERR(toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO));
+}
+
+static void setup_env (DB_ENV **envp, const char *envdir) {
+ { int chk_r = db_env_create(envp, 0); CKERR(chk_r); }
+ (*envp)->set_errfile(*envp, stderr);
+ { int chk_r = (*envp)->set_redzone(*envp, 0); CKERR(chk_r); }
+ { int chk_r = (*envp)->open(*envp, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void setup_env_and_prepare (DB_ENV **envp, const char *envdir, bool commit) {
+ DB *db;
+ DB_TXN *txn;
+ clean_env(envdir);
+ setup_env(envp, envdir);
+ CKERR(db_create(&db, *envp, 0));
+ CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO));
+ CKERR((*envp)->txn_begin(*envp, 0, &txn, 0));
+ DBT key;
+ dbt_init(&key, "foo", 4);
+ CKERR(db->put(db, txn, &key, &key, 0));
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ uint8_t gid[DB_GID_SIZE];
+ memset(gid, 0, DB_GID_SIZE);
+ gid[0]=42;
+ CKERR(txn->prepare(txn, gid, 0));
+ if (commit)
+ CKERR(txn->commit(txn, 0));
+}
+
+static void test (void) {
+ pid_t pid;
+
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env_and_prepare(&env, TOKU_TEST_FILENAME, false);
+ {
+ DB_PREPLIST l[1];
+ long count=-1;
+ CKERR(env->txn_recover(env, l, 1, &count, DB_FIRST));
+ printf("%s:%d count=%ld\n", __FILE__, __LINE__, count);
+ assert(count==1);
+ assert(l[0].gid[0]==42);
+ }
+ exit(0);
+ }
+ {
+ int status;
+ pid_t pid2 = wait(&status);
+ assert(pid2==pid);
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+ // Now run recovery and crash on purpose.
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+
+ // make sure there is 1 prepared txn.
+ {
+ DB_PREPLIST l[1];
+ long count=-1;
+ int r = env->txn_recover(env, l, 1, &count, DB_FIRST);
+ printf("r=%d count=%ld\n", r, count);
+ assert(count==1);
+ assert(l[0].gid[0]==42);
+ for (int i=1; i<DB_GID_SIZE; i++) {
+ assert(l[0].gid[i]==0);
+ }
+ }
+
+ exit(0);
+ }
+ {
+ int status;
+ pid_t pid2 = wait(&status);
+ assert(pid2==pid);
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+
+ // Now see if recovery works the second time.
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+ {
+ DB_PREPLIST l[1];
+ long count=-1;
+ int r = env->txn_recover(env, l, 1, &count, DB_FIRST);
+ printf("r=%d count=%ld\n", r, count);
+ assert(count==1);
+ assert(l[0].gid[0]==42);
+ for (int i=1; i<DB_GID_SIZE; i++) {
+ assert(l[0].gid[i]==0);
+ }
+ { int chk_r = l->txn->commit(l->txn, 0); CKERR(chk_r); }
+ }
+ { int chk_r = env ->close(env, 0); CKERR(chk_r); }
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+ // first test: open an environment, a db, a txn, and do a prepare. Then do txn_prepare (without even closing the environment).
+ test();
+
+
+ // second test: poen environment, a db, a txn, prepare, close the environment. Then reopen and do txn_prepare.
+
+ // third test: make sure there is an fsync on txn_prepare, but not on the following commit.
+
+
+ // Then close the environment Find out what BDB does when ask for the txn prepares.
+ // Other tests: read prepared txns, 1 at a time. Then close it and read them again.
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test-prepare3.cc b/storage/tokudb/PerconaFT/src/tests/test-prepare3.cc
new file mode 100644
index 00000000000..5cb3796a26b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test-prepare3.cc
@@ -0,0 +1,339 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/wait.h>
+
+// Verify that if we prepare a transaction, then commit a bunch more transactions so that the logs may have been rotated, then the transaction can commit or abort properly on recovery.
+
+static void clean_env (const char *envdir) {
+ const int len = strlen(envdir)+100;
+ char cmd[len];
+ snprintf(cmd, len, "rm -rf %s", envdir);
+ int r = system(cmd);
+ CKERR(r);
+ CKERR(toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO));
+}
+
+static void setup_env (DB_ENV **envp, const char *envdir) {
+ { int chk_r = db_env_create(envp, 0); CKERR(chk_r); }
+ (*envp)->set_errfile(*envp, stderr);
+ { int chk_r = (*envp)->set_redzone(*envp, 0); CKERR(chk_r); }
+ { int chk_r = (*envp)->open(*envp, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+#define NTXNS 6
+
+static void setup_env_and_prepare (DB_ENV **envp, const char *envdir) {
+ DB *db;
+ clean_env(envdir);
+ setup_env(envp, envdir);
+ CKERR(db_create(&db, *envp, 0));
+ CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO));
+
+ {
+ DB_TXN *txn;
+ CKERR((*envp)->txn_begin(*envp, 0, &txn, 0));
+ for (int tnum=0; tnum<NTXNS; tnum++) {
+ for (int k=0; k<26; k++) {
+ #define DSIZE 200
+ char data[DSIZE];
+ memset(data, ' ', DSIZE);
+ data[0]='a'+tnum;
+ data[1]='a'+k;
+ data[DSIZE-1]=0;
+ DBT key;
+ dbt_init(&key, data, DSIZE);
+ CKERR(db->put(db, txn, &key, &key, 0));
+ }
+ }
+ CKERR(txn->commit(txn, 0));
+ }
+
+ for (int tnum=0; tnum<NTXNS; tnum++) {
+ DB_TXN *txn;
+ CKERR((*envp)->txn_begin(*envp, 0, &txn, 0));
+ char data[3]={(char)('a'+tnum),'_',0};
+ DBT key;
+ dbt_init(&key, data, 3);
+ CKERR(db->put(db, txn, &key, &key, 0));
+ uint8_t gid[DB_GID_SIZE];
+ memset(gid, 0, DB_GID_SIZE);
+ gid[0]='a'+tnum;
+ CKERR(txn->prepare(txn, gid, 0));
+ // Drop txn on the ground, since we will commit or abort it after recovery
+ if (tnum==0) {
+ //printf("commit %d\n", tnum);
+ CKERR(txn->commit(txn, 0));
+ } else if (tnum==1) {
+ //printf("abort %d\n", tnum);
+ CKERR(txn->abort(txn));
+ } else {
+ //printf("prepare %d\n", tnum);
+ }
+ }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+}
+
+enum prepared_state {
+ COMMITTED,
+ ABORTED,
+ MAYBE_COMMITTED,
+ MAYBE_ABORTED,
+ PREPARED};
+
+
+static void check_prepared_list (enum prepared_state ps[NTXNS], long count, DB_PREPLIST *l) {
+ int count_prepared=0;
+ int count_maybe_prepared=0;
+ for (int j=0; j<NTXNS; j++) {
+ switch (ps[j]) {
+ case COMMITTED:
+ case ABORTED:
+ goto next;
+ case PREPARED:
+ count_prepared++;
+ case MAYBE_COMMITTED:
+ case MAYBE_ABORTED:
+ count_maybe_prepared++;
+ goto next;
+ }
+ assert(0);
+ next:;
+ }
+
+ assert(count>=count_prepared && count<=count_maybe_prepared);
+
+ bool found[NTXNS];
+ for (int j=0; j<NTXNS; j++) {
+ found[j] = (ps[j]!=PREPARED);
+ }
+
+ // now found[j] is false on those transactions that I hope to find in the prepared list.
+ for (int j=0; j<count; j++) {
+ int num = l[j].gid[0]-'a';
+ assert(num>=0 && num<NTXNS);
+ switch (ps[num]) {
+ case PREPARED:
+ assert(!found[num]);
+ found[num]=true;
+ break;
+ default:;
+ }
+ for (int i=1; i<DB_GID_SIZE; i++) {
+ assert(l[j].gid[i]==0);
+ }
+ }
+}
+
+static void get_prepared (DB_ENV *env, long *count, DB_PREPLIST *l) {
+ CKERR(env->txn_recover(env, l, NTXNS, count, DB_FIRST));
+ //printf("%s:%d count=%ld\n", __FILE__, __LINE__, *count);
+ assert(*count>=0);
+}
+
+static void check_prepared_txns (DB_ENV *env, enum prepared_state ps[NTXNS]) {
+ DB_PREPLIST l[NTXNS];
+ long count=-1;
+ get_prepared(env, &count, l);
+ check_prepared_list(ps, count, l);
+}
+
+static void check_state_after_full_recovery (DB_ENV *env) {
+ DB *db;
+ CKERR(db_create(&db, env, 0));
+ CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO));
+
+ for (int tnum=0; tnum<NTXNS; tnum++) {
+ DB_TXN *txn;
+ CKERR(env->txn_begin(env, 0, &txn, 0));
+ char data[3]={(char)('a'+tnum),'_',0};
+ DBT key;
+ dbt_init(&key, data, 3);
+ DBT dbt_data;
+ dbt_init(&dbt_data, NULL, 0);
+ int r = db->get(db, txn, &key, &dbt_data, 0);
+ if (tnum%2==0) {
+ assert(r==0);
+ assert(dbt_data.size==3 && memcmp(dbt_data.data, data, 3)==0);
+ } else {
+ assert(r==DB_NOTFOUND);
+ }
+ CKERR(txn->commit(txn, 0));
+ }
+ CKERR(db->close(db, 0));
+}
+
+static void waitfor (pid_t pid) {
+ int status;
+ pid_t pid2 = wait(&status);
+ assert(pid2==pid);
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+}
+
+static void abort_number(int num, int count, DB_PREPLIST *l) {
+ for (int j=0; j<count; j++) {
+ if (l[j].gid[0]=='a'+num) {
+ CKERR(l[j].txn->abort(l[j].txn));
+ return;
+ }
+ }
+ assert(0);
+}
+static void commit_number(int num, int count, DB_PREPLIST *l) {
+ for (int j=0; j<count; j++) {
+ if (l[j].gid[0]=='a'+num) {
+ CKERR(l[j].txn->commit(l[j].txn, 0));
+ return;
+ }
+ }
+ assert(0);
+}
+
+static void test (void) {
+ pid_t pid;
+
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env_and_prepare(&env, TOKU_TEST_FILENAME);
+ enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,PREPARED,PREPARED,PREPARED,PREPARED};
+ check_prepared_txns(env, prepared);
+ exit(0);
+ }
+ waitfor(pid);
+ // Now run recovery and crash on purpose.
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+ enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,PREPARED,PREPARED,PREPARED,PREPARED};
+ check_prepared_txns(env, prepared);
+ exit(0);
+ }
+ waitfor(pid);
+
+ // Now see if recovery works the second time.
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+ enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,PREPARED,PREPARED,PREPARED,PREPARED};
+ check_prepared_txns(env, prepared);
+ exit(0);
+ }
+ waitfor(pid);
+
+ // Now see if recovery works the third time.
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+ enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,PREPARED,PREPARED,PREPARED,PREPARED};
+ DB_PREPLIST l[NTXNS];
+ long count=-1;
+ get_prepared(env, &count, l);
+ check_prepared_list(prepared, count, l);
+ abort_number(3, count, l);
+ commit_number(2, count, l); // do the commit second so it will make it to disk.
+ exit(0);
+ }
+ waitfor(pid);
+ // Now see if recovery works a third time, with number 2 and 3 no longer in the prepared state.
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+ enum prepared_state prepared[NTXNS]={COMMITTED,ABORTED,MAYBE_COMMITTED,MAYBE_ABORTED,PREPARED,PREPARED};
+ DB_PREPLIST l[NTXNS];
+ long count=-1;
+ //printf("%s:%d count=%ld\n", __FILE__, __LINE__, count); // it's a little bit funky that the committed transactions in BDB (from commit_number(2,...) above) don't stay committed. But whatever...
+ get_prepared(env, &count, l);
+ check_prepared_list(prepared, count, l);
+ exit(0);
+ }
+ waitfor(pid);
+ // Now see if recovery works a fourth time, with number 2 and 3 no longer in the prepared state.
+ // This time we'll do get_prepared with a short count.
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+ //printf("%s:%d count=%ld\n", __FILE__, __LINE__, count); // it's a little bit funky that the committed transactions in BDB (from commit_number(2,...) above) don't stay committed. But whatever...
+
+ long actual_count=0;
+
+ for (int recover_num=0; 1; recover_num++) {
+ long count=-1;
+ DB_PREPLIST *MALLOC_N(1, l); // use malloc so that valgrind might notice a problem
+ CKERR(env->txn_recover(env, l, 1, &count, recover_num==0 ? DB_FIRST : DB_NEXT));
+ //printf("recover_num %d count=%ld\n", recover_num,count);
+ if (count==0) break;
+ actual_count++;
+ if ((l[0].gid[0]-'a')%2==0) {
+ CKERR(l[0].txn->commit(l[0].txn, 0));
+ } else {
+ CKERR(l[0].txn->abort(l[0].txn));
+ }
+ toku_free(l);
+ }
+ //printf("actual_count=%ld\n", actual_count);
+
+ // Now let's see what the state is.
+ check_state_after_full_recovery(env);
+
+ CKERR(env->close(env, 0));
+ exit(0);
+ }
+ waitfor(pid);
+ // Now we should end up with nothing in the recovery list.
+ {
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+ long count=-1;
+ DB_PREPLIST l[1];
+ CKERR(env->txn_recover(env, l, 1, &count, DB_FIRST));
+ assert(count==0);
+ check_state_after_full_recovery(env);
+ CKERR(env->close(env, 0));
+ }
+
+
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+ // first test: open an environment, a db, a txn, and do a prepare. Then do txn_prepare (without even closing the environment).
+ test();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test-rollinclude.cc b/storage/tokudb/PerconaFT/src/tests/test-rollinclude.cc
new file mode 100644
index 00000000000..0b7d9b57350
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test-rollinclude.cc
@@ -0,0 +1,118 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// insert enough rows with a child txn to force a rollinclude log entry
+
+static void
+populate(DB_ENV *env, DB *db, int nrows) {
+ int r;
+ DB_TXN *parent = NULL;
+ r = env->txn_begin(env, NULL, &parent, 0); assert_zero(r);
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, parent, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = htonl(i);
+ char kk[4096]; // 4 KB key
+ memset(kk, 0, sizeof kk);
+ memcpy(kk, &k, sizeof k);
+ DBT key = { .data = &kk, .size = sizeof kk };
+ DBT val = { .data = NULL, .size = 0 };
+ r = db->put(db, txn, &key, &val, 0);
+ assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+ r = parent->commit(parent, 0); assert_zero(r);
+}
+
+static void
+run_test(int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db = NULL;
+ r = db_create(&db, env, 0); assert_zero(r);
+
+ r = db->open(db, NULL, "0.tdb", NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert_zero(r);
+
+ populate(env, db, nrows);
+
+ r = db->close(db, 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int nrows = 1024; // = 4 MB / 4KB assumes 4 MB rollback nodes
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test-xa-prepare.cc b/storage/tokudb/PerconaFT/src/tests/test-xa-prepare.cc
new file mode 100644
index 00000000000..ba04be56716
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test-xa-prepare.cc
@@ -0,0 +1,157 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/wait.h>
+
+static void clean_env (const char *envdir) {
+ const int len = strlen(envdir)+100;
+ char cmd[len];
+ snprintf(cmd, len, "rm -rf %s", envdir);
+ int r = system(cmd);
+ CKERR(r);
+ CKERR(toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO));
+}
+
+static void setup_env (DB_ENV **envp, const char *envdir) {
+ { int chk_r = db_env_create(envp, 0); CKERR(chk_r); }
+ (*envp)->set_errfile(*envp, stderr);
+ { int chk_r = (*envp)->set_redzone(*envp, 0); CKERR(chk_r); }
+ { int chk_r = (*envp)->open(*envp, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+const unsigned int myformatid = 0x74736554;
+
+static void setup_env_and_prepare (DB_ENV **envp, const char *envdir, bool commit) {
+ DB *db;
+ DB_TXN *txn;
+ clean_env(envdir);
+ setup_env(envp, envdir);
+ CKERR(db_create(&db, *envp, 0));
+ CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO));
+ CKERR((*envp)->txn_begin(*envp, 0, &txn, 0));
+ DBT key;
+ dbt_init(&key, "foo", 4);
+ CKERR(db->put(db, txn, &key, &key, 0));
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ TOKU_XA_XID x = {.formatID = myformatid,
+ .gtrid_length = 8,
+ .bqual_length = 9};
+ for (int i=0; i<8+9; i++) x.data[i] = 42+i;
+ CKERR(txn->xa_prepare(txn, &x, 0));
+ if (commit)
+ CKERR(txn->commit(txn, 0));
+}
+
+static void test1 (void) {
+ pid_t pid;
+ bool do_fork = true;
+ if (!do_fork || 0==(pid=fork())) {
+ DB_ENV *env;
+ setup_env_and_prepare(&env, TOKU_TEST_FILENAME, false);
+ {
+ TOKU_XA_XID l[1];
+ long count=-1;
+ CKERR(env->txn_xa_recover(env, l, 1, &count, DB_FIRST));
+ printf("%s:%d count=%ld\n", __FILE__, __LINE__, count);
+ assert(count==1);
+ assert(myformatid==l[0].formatID);
+ assert( 8==l[0].gtrid_length);
+ assert( 9==l[0].bqual_length);
+ for (int i=0; i<8+9; i++) {
+ assert(l[0].data[i]==42+i);
+ }
+ }
+ exit(0);
+ }
+ int status;
+ if (do_fork) {
+ pid_t pid2 = wait(&status);
+ assert(pid2==pid);
+ }
+
+ DB_ENV *env2;
+ char envdir2[TOKU_PATH_MAX+1];
+ setup_env_and_prepare(&env2, toku_path_join(envdir2, 2, TOKU_TEST_FILENAME, "envdir2"), true);
+
+ // Now we can look at env2 in the debugger to see if we managed to make it the same
+
+ DB_ENV *env;
+ setup_env(&env, TOKU_TEST_FILENAME);
+
+ {
+ TOKU_XA_XID l[1];
+ long count=-1;
+ {
+ int r = env->txn_xa_recover(env, l, 1, &count, DB_FIRST);
+ printf("r=%d count=%ld\n", r, count);
+ }
+ assert(count==1);
+ assert(l[0].data[0]==42);
+ assert(myformatid==l[0].formatID);
+ assert( 8 ==l[0].gtrid_length);
+ assert( 9 ==l[0].bqual_length);
+ for (int i=0; i<8+9; i++) {
+ assert(l[0].data[i]==42+i);
+ }
+ {
+ DB_TXN *txn;
+ int r = env->get_txn_from_xid(env, &l[0], &txn);
+ assert(r==0);
+ { int chk_r = txn->commit(txn, 0); CKERR(chk_r); }
+ }
+ }
+ { int chk_r = env2->close(env2, 0); CKERR(chk_r); }
+ { int chk_r = env ->close(env, 0); CKERR(chk_r); }
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+ // first test: open an environment, a db, a txn, and do a prepare. Then do txn_prepare (without even closing the environment).
+ test1();
+
+
+ // second test: poen environment, a db, a txn, prepare, close the environment. Then reopen and do txn_prepare.
+
+ // third test: make sure there is an fsync on txn_prepare, but not on the following commit.
+
+
+ // Then close the environment Find out what BDB does when ask for the txn prepares.
+ // Other tests: read prepared txns, 1 at a time. Then close it and read them again.
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test-xopen-eclose.cc b/storage/tokudb/PerconaFT/src/tests/test-xopen-eclose.cc
new file mode 100644
index 00000000000..6b585b3a6c7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test-xopen-eclose.cc
@@ -0,0 +1,139 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the env close aborts open txns
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+static void run_test (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+
+ r = env->close(env, 0);
+ assert(r == EINVAL);
+
+#if 0
+ r = txn->abort(txn); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+#endif
+}
+
+static void run_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ // run recovery
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags + DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+static void run_no_recover (void) {
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags & ~DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ exit(0);
+}
+
+const char *cmd;
+
+bool do_test=false, do_recover=false, do_recover_only=false, do_no_recover = false;
+
+static void test_parse_args (int argc, char *const argv[]) {
+ int resultcode;
+ cmd = argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0], "-v") == 0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "--test")==0) {
+ do_test=true;
+ } else if (strcmp(argv[0], "--recover") == 0) {
+ do_recover=true;
+ } else if (strcmp(argv[0], "--recover-only") == 0) {
+ do_recover_only=true;
+ } else if (strcmp(argv[0], "--no-recover") == 0) {
+ do_no_recover=true;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q]* [-h] {--test | --recover } \n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main (int argc, char *const argv[]) {
+ test_parse_args(argc, argv);
+ if (do_test) {
+ run_test();
+ } else if (do_recover) {
+ run_recover();
+ } else if (do_recover_only) {
+ run_recover();
+ } else if (do_no_recover) {
+ run_no_recover();
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test.h b/storage/tokudb/PerconaFT/src/tests/test.h
new file mode 100644
index 00000000000..ff464f55890
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test.h
@@ -0,0 +1,454 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+#include <toku_portability.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <toku_stdint.h>
+#include <stdio.h>
+#include <db.h>
+#include <limits.h>
+#include <errno.h>
+#include <toku_htonl.h>
+#include <portability/toku_path.h>
+#include <portability/toku_crash.h>
+#include "toku_assert.h"
+#include <signal.h>
+#include <time.h>
+
+#include "ydb.h"
+//TDB uses DB_NOTFOUND for c_del and DB_CURRENT errors.
+#ifdef DB_KEYEMPTY
+#error
+#endif
+#define DB_KEYEMPTY DB_NOTFOUND
+
+// Certain tests fail when row locks taken for read are not shared.
+// This switch prevents them from failing so long as read locks are not shared.
+#define BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+
+int verbose=0;
+
+#define UU(x) x __attribute__((__unused__))
+
+#define CKERR(r) ({ int __r = r; if (__r!=0) fprintf(stderr, "%s:%d error %d %s\n", __FILE__, __LINE__, __r, db_strerror(r)); assert(__r==0); })
+#define CKERR2(r,r2) do { if (r!=r2) fprintf(stderr, "%s:%d error %d %s, expected %d\n", __FILE__, __LINE__, r, db_strerror(r), r2); assert(r==r2); } while (0)
+#define CKERR2s(r,r2,r3) do { if (r!=r2 && r!=r3) fprintf(stderr, "%s:%d error %d %s, expected %d or %d\n", __FILE__, __LINE__, r, db_strerror(r), r2,r3); assert(r==r2||r==r3); } while (0)
+
+/*
+ * Helpers for defining pseudo-hygienic macros using a (gensym)-like
+ * technique.
+ */
+#define _CONCAT(x, y) x ## y
+#define CONCAT(x, y) _CONCAT(x, y)
+#define GS(symbol) CONCAT(CONCAT(__gensym_, __LINE__), CONCAT(_, symbol))
+
+#define DEBUG_LINE do { \
+ fprintf(stderr, "%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); \
+ fflush(stderr); \
+} while (0)
+
+static __attribute__((__unused__)) void
+parse_args (int argc, char * const argv[]) {
+ const char *argv0=argv[0];
+ while (argc>1) {
+ int resultcode=0;
+ if (strcmp(argv[1], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[1],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[1], "-h")==0) {
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q] [-h]\n", argv0);
+ exit(resultcode);
+ } else {
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+static __attribute__((__unused__)) void
+print_engine_status(DB_ENV * UU(env)) {
+ if (verbose) { // verbose declared statically in this file
+ uint64_t nrows;
+ env->get_engine_status_num_rows(env, &nrows);
+ int bufsiz = nrows * 128; // assume 128 characters per row
+ char buff[bufsiz];
+ env->get_engine_status_text(env, buff, bufsiz);
+ printf("Engine status:\n");
+ printf("%s", buff);
+ }
+}
+
+static __attribute__((__unused__)) uint64_t
+get_engine_status_val(DB_ENV * UU(env), const char * keyname) {
+ uint64_t rval = 0;
+ uint64_t nrows;
+ uint64_t max_rows;
+ env->get_engine_status_num_rows(env, &max_rows);
+ TOKU_ENGINE_STATUS_ROW_S mystat[max_rows];
+ fs_redzone_state redzone_state;
+ uint64_t panic;
+ uint32_t panic_string_len = 1024;
+ char panic_string[panic_string_len];
+ int r = env->get_engine_status (env, mystat, max_rows, &nrows, &redzone_state, &panic, panic_string, panic_string_len, TOKU_ENGINE_STATUS);
+ CKERR(r);
+ int found = 0;
+ for (uint64_t i = 0; i < nrows && !found; i++) {
+ if (strcmp(keyname, mystat[i].keyname) == 0) {
+ found++;
+ rval = mystat[i].value.num;
+ }
+ }
+ CKERR2(found, 1);
+ return rval;
+}
+
+static __attribute__((__unused__)) DBT *
+dbt_init(DBT *dbt, const void *data, uint32_t size) {
+ memset(dbt, 0, sizeof *dbt);
+ dbt->data = (void*)data;
+ dbt->size = size;
+ return dbt;
+}
+
+static __attribute__((__unused__)) DBT *
+dbt_init_malloc (DBT *dbt) {
+ memset(dbt, 0, sizeof *dbt);
+ dbt->flags = DB_DBT_MALLOC;
+ return dbt;
+}
+
+static __attribute__((__unused__)) DBT *
+dbt_init_realloc (DBT *dbt) {
+ memset(dbt, 0, sizeof *dbt);
+ dbt->flags = DB_DBT_REALLOC;
+ return dbt;
+}
+
+// Simple LCG random number generator. Not high quality, but good enough.
+static uint32_t rstate=1;
+static inline void mysrandom (int s) {
+ rstate=s;
+}
+static inline uint32_t myrandom (void) {
+ rstate = (279470275ull*(uint64_t)rstate)%4294967291ull;
+ return rstate;
+}
+
+static __attribute__((__unused__)) int
+int64_dbt_cmp (DB *db UU(), const DBT *a, const DBT *b) {
+// assert(db && a && b);
+ assert(a);
+ assert(b);
+// assert(db);
+
+ assert(a->size == sizeof(int64_t));
+ assert(b->size == sizeof(int64_t));
+
+ int64_t x = *(int64_t *) a->data;
+ int64_t y = *(int64_t *) b->data;
+
+ if (x<y) return -1;
+ if (x>y) return 1;
+ return 0;
+}
+
+static __attribute__((__unused__)) int
+int_dbt_cmp (DB *db, const DBT *a, const DBT *b) {
+ assert(db && a && b);
+ assert(a->size == sizeof(int));
+ assert(b->size == sizeof(int));
+
+ int x = *(int *) a->data;
+ int y = *(int *) b->data;
+
+ if (x<y) return -1;
+ if (x>y) return 1;
+ return 0;
+}
+
+static __attribute__((__unused__)) int
+uint_dbt_cmp (DB *db, const DBT *a, const DBT *b) {
+ assert(db && a && b);
+ assert(a->size == sizeof(unsigned int));
+ assert(b->size == sizeof(unsigned int));
+
+ unsigned int x = *(unsigned int *) a->data;
+ unsigned int y = *(unsigned int *) b->data;
+
+ if (x<y) return -1;
+ if (x>y) return 1;
+ return 0;
+}
+
+#define SET_TRACE_FILE(x) toku_set_trace_file(x)
+#define CLOSE_TRACE_FILE(x) toku_close_trace_file()
+
+#include <memory.h>
+
+static uint64_t __attribute__((__unused__))
+random64(void) {
+ const unsigned int seed = 0xFEEDFACE;
+ static int seeded = 0;
+ if (!seeded) {
+ seeded = 1;
+ srandom(seed);
+ }
+ //random() generates 31 bits of randomness (low order)
+ uint64_t low = random();
+ uint64_t high = random();
+ uint64_t twobits = random();
+ uint64_t ret = low | (high<<31) | (twobits<<62);
+ return ret;
+}
+
+static __attribute__((__unused__))
+double get_tdiff(void) {
+ static struct timeval prev={0,0};
+ if (prev.tv_sec==0) {
+ gettimeofday(&prev, 0);
+ return 0.0;
+ } else {
+ struct timeval now;
+ gettimeofday(&now, 0);
+ double diff = now.tv_sec - prev.tv_sec + 1e-6*(now.tv_usec - prev.tv_usec);
+ prev = now;
+ return diff;
+ }
+}
+
+static __attribute__((__unused__))
+void format_time(const time_t *timer, char *buf) {
+ ctime_r(timer, buf);
+ size_t len = strlen(buf);
+ assert(len < 26);
+ char end;
+
+ assert(len>=1);
+ end = buf[len-1];
+ while (end == '\n' || end == '\r') {
+ buf[len-1] = '\0';
+ len--;
+ assert(len>=1);
+ end = buf[len-1];
+ }
+}
+
+static __attribute__((__unused__))
+void print_time_now(void) {
+ char timestr[80];
+ time_t now = time(NULL);
+ format_time(&now, timestr);
+ printf("%s", timestr);
+}
+
+static void UU()
+multiply_locks_for_n_dbs(DB_ENV *env, int num_dbs) {
+ uint64_t current_max_lock_memory;
+ int r = env->get_lk_max_memory(env, &current_max_lock_memory);
+ CKERR(r);
+ r = env->set_lk_max_memory(env, current_max_lock_memory * num_dbs);
+ CKERR(r);
+}
+
+static inline void
+default_parse_args (int argc, char * const argv[]) {
+ const char *progname=argv[0];
+ argc--; argv++;
+ while (argc>0) {
+ if (strcmp(argv[0],"-v")==0) {
+ ++verbose;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose=0;
+ } else {
+ fprintf(stderr, "Usage:\n %s [-v] [-q]\n", progname);
+ exit(1);
+ }
+ argc--; argv++;
+ }
+}
+
+UU()
+static void copy_dbt(DBT *dest, const DBT *src) {
+ assert(dest->flags & DB_DBT_REALLOC);
+ dest->data = toku_xrealloc(dest->data, src->size);
+ dest->size = src->size;
+ memcpy(dest->data, src->data, src->size);
+}
+
+// DBT_ARRAY is a toku-specific type
+UU()
+static int
+env_update_multiple_test_no_array(
+ DB_ENV *env,
+ DB *src_db,
+ DB_TXN *txn,
+ DBT *old_src_key, DBT *old_src_data,
+ DBT *new_src_key, DBT *new_src_data,
+ uint32_t num_dbs, DB **db_array, uint32_t* flags_array,
+ uint32_t num_keys, DBT keys[],
+ uint32_t num_vals, DBT vals[]) {
+ int r;
+ DBT_ARRAY key_arrays[num_keys];
+ DBT_ARRAY val_arrays[num_vals];
+ for (uint32_t i = 0; i < num_keys; i++) {
+ toku_dbt_array_init(&key_arrays[i], 1);
+ key_arrays[i].dbts[0] = keys[i];
+ }
+ for (uint32_t i = 0; i < num_vals; i++) {
+ toku_dbt_array_init(&val_arrays[i], 1);
+ val_arrays[i].dbts[0] = vals[i];
+ }
+ r = env->update_multiple(env, src_db, txn, old_src_key, old_src_data, new_src_key, new_src_data,
+ num_dbs, db_array, flags_array,
+ num_keys, &key_arrays[0],
+ num_vals, &val_arrays[0]);
+ for (uint32_t i = 0; i < num_keys; i++) {
+ invariant(key_arrays[i].size == 1);
+ invariant(key_arrays[i].capacity == 1);
+ keys[i] = key_arrays[i].dbts[0];
+ toku_dbt_array_destroy_shallow(&key_arrays[i]);
+ }
+ for (uint32_t i = 0; i < num_vals; i++) {
+ invariant(val_arrays[i].size == 1);
+ invariant(val_arrays[i].capacity == 1);
+ vals[i] = val_arrays[i].dbts[0];
+ toku_dbt_array_destroy_shallow(&val_arrays[i]);
+ }
+ return r;
+}
+
+UU()
+static int env_put_multiple_test_no_array(
+ DB_ENV *env,
+ DB *src_db,
+ DB_TXN *txn,
+ const DBT *src_key,
+ const DBT *src_val,
+ uint32_t num_dbs,
+ DB **db_array,
+ DBT *keys,
+ DBT *vals,
+ uint32_t *flags_array)
+{
+ int r;
+ DBT_ARRAY key_arrays[num_dbs];
+ DBT_ARRAY val_arrays[num_dbs];
+ for (uint32_t i = 0; i < num_dbs; i++) {
+ toku_dbt_array_init(&key_arrays[i], 1);
+ toku_dbt_array_init(&val_arrays[i], 1);
+ key_arrays[i].dbts[0] = keys[i];
+ val_arrays[i].dbts[0] = vals[i];
+ }
+ r = env->put_multiple(env, src_db, txn, src_key, src_val, num_dbs, db_array, &key_arrays[0], &val_arrays[0], flags_array);
+ for (uint32_t i = 0; i < num_dbs; i++) {
+ invariant(key_arrays[i].size == 1);
+ invariant(key_arrays[i].capacity == 1);
+ invariant(val_arrays[i].size == 1);
+ invariant(val_arrays[i].capacity == 1);
+ keys[i] = key_arrays[i].dbts[0];
+ vals[i] = val_arrays[i].dbts[0];
+ toku_dbt_array_destroy_shallow(&key_arrays[i]);
+ toku_dbt_array_destroy_shallow(&val_arrays[i]);
+ }
+ return r;
+}
+
+UU()
+static int env_del_multiple_test_no_array(
+ DB_ENV *env,
+ DB *src_db,
+ DB_TXN *txn,
+ const DBT *src_key,
+ const DBT *src_val,
+ uint32_t num_dbs,
+ DB **db_array,
+ DBT *keys,
+ uint32_t *flags_array)
+{
+ int r;
+ DBT_ARRAY key_arrays[num_dbs];
+ for (uint32_t i = 0; i < num_dbs; i++) {
+ toku_dbt_array_init(&key_arrays[i], 1);
+ key_arrays[i].dbts[0] = keys[i];
+ }
+ r = env->del_multiple(env, src_db, txn, src_key, src_val, num_dbs, db_array, &key_arrays[0], flags_array);
+ for (uint32_t i = 0; i < num_dbs; i++) {
+ invariant(key_arrays[i].size == 1);
+ invariant(key_arrays[i].capacity == 1);
+ keys[i] = key_arrays[i].dbts[0];
+ toku_dbt_array_destroy_shallow(&key_arrays[i]);
+ }
+ return r;
+}
+
+/* Some macros for evaluating blocks or functions within the scope of a
+ * transaction. */
+#define IN_TXN_COMMIT(env, parent, txn, flags, expr) ({ \
+ DB_TXN *(txn); \
+ { int chk_r = (env)->txn_begin((env), (parent), &(txn), (flags)); CKERR(chk_r); } \
+ (expr); \
+ { int chk_r = (txn)->commit((txn), 0); CKERR(chk_r); } \
+ })
+
+#define IN_TXN_ABORT(env, parent, txn, flags, expr) ({ \
+ DB_TXN *(txn); \
+ { int chk_r = (env)->txn_begin((env), (parent), &(txn), (flags)); CKERR(chk_r); } \
+ (expr); \
+ { int chk_r = (txn)->abort(txn); CKERR(chk_r); } \
+ })
+
+int test_main(int argc, char *const argv[]);
+int main(int argc, char *const argv[]) {
+ int r;
+ toku_os_initialize_settings(1);
+ r = test_main(argc, argv);
+ return r;
+}
+
+#ifndef DB_GID_SIZE
+#define DB_GID_SIZE DB_XIDDATASIZE
+#endif
diff --git a/storage/tokudb/PerconaFT/src/tests/test1572.cc b/storage/tokudb/PerconaFT/src/tests/test1572.cc
new file mode 100644
index 00000000000..584b3991790
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test1572.cc
@@ -0,0 +1,112 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Is it feasible to run 4 billion transactions in one test in the regression tests? */
+#include <db.h>
+#include <sys/stat.h>
+#include <ft/logger/log.h>
+#include <src/ydb_txn.h>
+
+static void
+four_billion_subtransactions (int do_something_in_children, int use_big_increment) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *xparent;
+
+ uint64_t extra_increment;
+ if (use_big_increment) {
+ extra_increment = (1<<28); // 1/4 of a billion, so 16 transactions should push us over the edge.
+ } else {
+ extra_increment = 0; // xid is already incrementing once per txn.
+ }
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+
+ {
+ DB_TXN *txn;
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+ }
+
+ r=env->txn_begin(env, 0, &xparent, 0); CKERR(r);
+ long long i;
+ long long const fourbillion = use_big_increment ? 32 : 500000; // if using the big increment we should run into trouble in only 32 transactions or less.
+ for (i=0; i < fourbillion + 100; i++) {
+ DB_TXN *xchild;
+ toku_increase_last_xid(env, extra_increment);
+ r=env->txn_begin(env, xparent, &xchild, 0); CKERR(r);
+ if (do_something_in_children) {
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%lld", i);
+ snprintf(there, sizeof(there), "there%lld", i);
+ DBT key, val;
+ r=db->put(db, xchild,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&val, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+ }
+ r=xchild->commit(xchild, 0); CKERR(r);
+ }
+ r=xparent->commit(xparent, 0); CKERR(r);
+
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char *const argv[])
+{
+ parse_args(argc, argv);
+ four_billion_subtransactions(0, 0);
+ four_billion_subtransactions(1, 0);
+ four_billion_subtransactions(0, 1);
+ four_billion_subtransactions(1, 1);
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test1753.cc b/storage/tokudb/PerconaFT/src/tests/test1753.cc
new file mode 100644
index 00000000000..552540cde17
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test1753.cc
@@ -0,0 +1,90 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+
+
+DB_TXN *null_txn=0;
+
+static void do_test1753 (int do_create_on_reopen) {
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ // Create an empty file
+ {
+ DB_ENV *env;
+ DB *db;
+
+ const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_PRIVATE ;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, "main", 0, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ }
+ // Now open the empty file and insert
+ {
+ DB_ENV *env;
+ int envflags = DB_INIT_MPOOL| DB_THREAD |DB_PRIVATE;
+ if (do_create_on_reopen) envflags |= DB_CREATE;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, 0);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (do_create_on_reopen) CKERR(r);
+ else CKERR2(r, ENOENT);
+ r = env->close(env, 0); CKERR(r);
+
+ }
+}
+
+int test_main (int argc __attribute__((__unused__)), char * const argv[] __attribute__((__unused__))) {
+ do_test1753(1);
+ do_test1753(0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test1842.cc b/storage/tokudb/PerconaFT/src/tests/test1842.cc
new file mode 100644
index 00000000000..154ab61251b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test1842.cc
@@ -0,0 +1,177 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+static DB *db;
+static DB_ENV *env;
+
+static __attribute__((__unused__)) int
+length_int_dbt_cmp (DB *db_compare, const DBT *a, const DBT *b) {
+ assert(db_compare && a && b);
+ assert(a->size > sizeof(int));
+ assert(b->size > sizeof(int));
+
+ int i;
+ int extra_len_a = *(uint8_t*)((uint8_t*)a->data +4);
+ assert(a->size == sizeof(int)+extra_len_a);
+
+ for (i = 1; i < extra_len_a; i++) {
+ assert(((char*)a->data+4)[i] == ' ');
+ }
+
+ int extra_len_b = *(uint8_t*)((uint8_t*)b->data+4);
+ assert(b->size == sizeof(int)+extra_len_b);
+ for (i = 1; i < extra_len_b; i++) {
+ assert(((char*)b->data+4)[i] == ' ');
+ }
+
+ int x = *(int *) a->data;
+ int y = *(int *) b->data;
+
+ if (x<y) return -1;
+ if (x>y) return 1;
+
+ if (extra_len_a<extra_len_b) return -1;
+ if (extra_len_a>extra_len_b) return 1;
+ return 0;
+}
+
+static void
+setup_db (uint32_t dup_mode) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, int_dbt_cmp); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_flags(db, dup_mode); assert(r == 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+}
+
+static inline DBT *
+dbt_init_length(DBT *dbt, int val, uint8_t extra_len, uint8_t* buf) {
+ *(int*)buf = val;
+ buf[sizeof(int)] = extra_len;
+ int i;
+ for (i = 1; i < extra_len; i++) {
+ buf[sizeof(int)+i] = ' ';
+ }
+ return dbt_init(dbt, buf, sizeof(int)+extra_len);
+}
+
+static void
+test_txn_abort (uint32_t dup_mode) {
+ setup_db(dup_mode);
+ DBT key, val;
+ int r;
+
+
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ uint8_t value1[256];
+ uint8_t value2[256];
+
+ int k = 1;
+ int v1 = 1;
+ int v2 = 1;
+ uint8_t extra_1 = 1;
+ uint8_t extra_2 = 2;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init_length(&val, v1, extra_1, value1), 0);
+ CKERR(r);
+ r = txn->commit(txn, DB_TXN_NOSYNC);
+ txn = NULL;
+
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init_length(&val, v2, extra_2, value2), 0);
+ CKERR(r);
+ r = db->del(db, txn, dbt_init(&key, &k, sizeof k), DB_DELETE_ANY);
+ CKERR(r);
+ //Flush by scanning
+ {
+ DBC *c;
+ r = db->cursor(db, txn, &c, 0);
+ CKERR(r);
+ DBT ck;
+ DBT cv;
+ memset(&ck, 0, sizeof(ck));
+ memset(&cv, 0, sizeof(cv));
+ do {
+ r = c->c_get(c, &ck, &cv, DB_NEXT);
+ } while (r==0);
+ CKERR2(r, DB_NOTFOUND);
+ r = c->c_close(c);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_txn_abort(0);
+// test_txn_abort(DB_DUP|DB_DUPSORT);
+ return 0;
+}
+/*
+BNC_NBYTESINBUF
+comparison does assert that 'val(value) == length(value)'
+insert 1,'1' (commit it)
+ 1,'2 '
+do a db->del (1) we'll crash on the assert
+*/
diff --git a/storage/tokudb/PerconaFT/src/tests/test3039.cc b/storage/tokudb/PerconaFT/src/tests/test3039.cc
new file mode 100644
index 00000000000..54a2b3bf730
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test3039.cc
@@ -0,0 +1,278 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* This is a performance test. Releasing lock during I/O should mean that given two threads doing queries,
+ * and one of them is in-memory and one of them is out of memory, then the in-memory one should not be slowed down by the out-of-memory one.
+ *
+ * Step 1: Create a dictionary that doesn't fit in main memory. Do it fast (sequential insertions).
+ * Step 2: Measure performance of in-memory requests.
+ * Step 3: Add a thread that does requests in parallel.
+ */
+
+#include "test.h"
+#include <string.h>
+#include <toku_time.h>
+#include <toku_pthread.h>
+#include <portability/toku_atomic.h>
+
+static const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+#define ROWSIZE 100
+static const char dbname[] = "data.db";
+static unsigned long long n_rows;
+
+static DB_ENV *env = NULL;
+static DB *db;
+
+// BDB cannot handle big transactions by default (runs out of locks).
+#define N_PER_XACTION 10000
+
+static void create_db (uint64_t N) {
+ n_rows = N;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ { int r = db_env_create(&env, 0); CKERR(r); }
+ env->set_errfile(env, stderr);
+ env->set_redzone(env, 0);
+ { int r = env->set_cachesize(env, 0, 400*4096, 1); CKERR(r); }
+ { int r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r); }
+ DB_TXN *txn;
+ { int r = env->txn_begin(env, NULL, &txn, 0); CKERR(r); }
+ { int r = db_create(&db, env, 0); CKERR(r); }
+ { int r = db->set_pagesize(db, 4096); CKERR(r); }
+ { int r = db->open(db, txn, dbname, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r); }
+ { int r = txn->commit(txn, DB_TXN_NOSYNC); CKERR(r); }
+
+ { int r = env->txn_begin(env, NULL, &txn, 0); CKERR(r); }
+ uint64_t n_since_commit = 0;
+ for (unsigned long long i=0; i<N; i++) {
+ if (n_since_commit++ > N_PER_XACTION) {
+ { int r = txn->commit(txn, DB_TXN_NOSYNC); CKERR(r); }
+ { int r = env->txn_begin(env, NULL, &txn, 0); CKERR(r); }
+ }
+ char key[20];
+ char data[200];
+ snprintf(key, sizeof(key), "%016llx", i);
+ snprintf(data, sizeof(data), "%08lx%08lx%66s", random(), random()%16, "");
+ DBT keyd, datad;
+ {
+ int r = db->put(db, txn, dbt_init(&keyd, key, strlen(key)+1), dbt_init(&datad, data, strlen(data)+1), 0);
+ CKERR(r);
+ }
+ }
+ //printf("n_rows=%lld\n", n_rows);
+ { int r = txn->commit(txn, DB_TXN_NOSYNC); CKERR(r); }
+}
+
+struct reader_thread_state {
+ /* output */
+ double elapsed_time;
+ unsigned long long n_did_read;
+
+ /* input */
+ signed long long n_to_read; // Negative if we just run forever
+ int do_local;
+
+ /* communicate to the thread while running */
+ volatile int finish;
+
+};
+
+static
+void* reader_thread (void *arg)
+// Return the time to read
+{
+ struct timeval start_time, end_time;
+ gettimeofday(&start_time, 0);
+
+ DB_TXN *txn;
+ struct reader_thread_state *rs = (struct reader_thread_state *)arg;
+
+ { int r = env->txn_begin(env, NULL, &txn, 0); CKERR(r); }
+ char key[20];
+ char data [200];
+ DBT keyd, datad;
+ keyd.data = key;
+ keyd.size = 0;
+ keyd.ulen = sizeof(key);
+ keyd.flags = DB_DBT_USERMEM;
+ datad.data = data;
+ datad.size = 0;
+ datad.ulen = sizeof(data);
+ datad.flags = DB_DBT_USERMEM;
+
+#define N_DISTINCT 16
+ unsigned long long vals[N_DISTINCT];
+ if (rs->do_local) {
+ for (int i=0; i<N_DISTINCT; i++) {
+ vals[i] = random()%n_rows;
+ }
+ }
+
+ uint64_t n_since_commit = 0;
+ long long n_read_so_far = 0;
+ while ((!rs->finish) && ((rs->n_to_read < 0) || (n_read_so_far < rs->n_to_read))) {
+
+ if (n_since_commit++ > N_PER_XACTION) {
+ { int r = txn->commit(txn, DB_TXN_NOSYNC); CKERR(r); }
+ { int r = env->txn_begin(env, NULL, &txn, 0); CKERR(r); }
+ n_since_commit = 0;
+ }
+ long long value;
+ if (rs->do_local) {
+ long which = random()%N_DISTINCT;
+ value = vals[which];
+ //printf("value=%lld\n", value);
+ } else {
+ value = random()%n_rows;
+ }
+ snprintf(key, sizeof(key), "%016llx", value);
+ keyd.size = strlen(key)+1;
+ int r = db->get(db, txn, &keyd, &datad, 0);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ invariant(r == 0 || r == DB_LOCK_NOTGRANTED || r == DB_LOCK_DEADLOCK);
+#else
+ CKERR(r);
+#endif
+ rs->n_did_read++;
+ n_read_so_far ++;
+ }
+ { int r = txn->commit(txn, DB_TXN_NOSYNC); CKERR(r); }
+
+ gettimeofday(&end_time, 0);
+ rs->elapsed_time = toku_tdiff(&end_time, &start_time);
+ return NULL;
+}
+
+static
+void do_threads (unsigned long long N, int do_nonlocal) {
+ toku_pthread_t ths[2];
+ struct reader_thread_state rstates[2] = {{.elapsed_time = 0.0,
+ .n_did_read = 0,
+ .n_to_read = (long long signed) N,
+ .do_local = 1,
+ .finish = 0},
+ {.elapsed_time = 0.0,
+ .n_did_read = 0,
+ .n_to_read = -1,
+ .do_local = 0,
+ .finish = 0}};
+ int n_to_create = do_nonlocal ? 2 : 1;
+ for (int i=0; i<n_to_create; i++) {
+ int r = toku_pthread_create(&ths[i], 0, reader_thread, (void*)&rstates[i]);
+ CKERR(r);
+ }
+ for (int i=0; i<n_to_create; i++) {
+ void *retval;
+ int r = toku_pthread_join(ths[i], &retval);
+ CKERR(r);
+ assert(retval==0);
+ if (verbose) {
+ printf("%9s thread time = %8.2fs on %9lld reads (%.3f us/read)\n",
+ (i==0 ? "local" : "nonlocal"),
+ rstates[i].elapsed_time, rstates[i].n_did_read, rstates[i].elapsed_time/rstates[i].n_did_read * 1e6);
+ }
+ rstates[1].finish = 1;
+ }
+ if (verbose && do_nonlocal) {
+ printf("total %9lld reads (%.3f us/read)\n",
+ rstates[0].n_did_read + rstates[1].n_did_read,
+ (rstates[0].elapsed_time)/(rstates[0].n_did_read + rstates[1].n_did_read) * 1e6);
+ }
+}
+
+static volatile unsigned long long n_preads;
+
+static ssize_t my_pread (int fd, void *buf, size_t count, off_t offset) {
+ (void) toku_sync_fetch_and_add(&n_preads, 1);
+ usleep(1000); // sleep for a millisecond
+ return pread(fd, buf, count, offset);
+}
+
+unsigned long N_default = 100000;
+unsigned long N;
+
+static void my_parse_args (int argc, char * const argv[]) {
+ const char *progname = argv[0];
+ argc--; argv++;
+ verbose = 0;
+ N = N_default;
+ while (argc>0) {
+ if (strcmp(argv[0],"-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ if (verbose>0) verbose--;
+ } else if (strcmp(argv[0],"-n")==0) {
+ argc--; argv++;
+ if (argc==0) goto usage;
+ errno = 0;
+ char *end;
+ N = strtol(argv[0], &end, 10);
+ if (errno!=0 || *end!=0) goto usage;
+ } else {
+ usage:
+ fprintf(stderr, "Usage:\n %s [-v] [-q] [-n <rowcount> (default %ld)]\n", progname, N_default);
+ fprintf(stderr, " -n 10000 is probably good for valgrind.\n");
+ exit(1);
+ }
+ argc--; argv++;
+ }
+
+}
+
+int test_main (int argc, char * const argv[]) {
+ my_parse_args(argc, argv);
+
+ unsigned long long M = N*10;
+
+ db_env_set_func_pread(my_pread);
+
+ create_db (N);
+ if (verbose) printf("%lld preads\n", n_preads);
+ do_threads (M, 0);
+ if (verbose) printf("%lld preads\n", n_preads);
+ do_threads (M, 0);
+ if (verbose) printf("%lld preads\n", n_preads);
+ do_threads (M, 1);
+ if (verbose) printf("%lld preads\n", n_preads);
+ { int r = db->close(db, 0); CKERR(r); }
+ { int r = env->close(env, 0); CKERR(r); }
+ if (verbose) printf("%lld preads\n", n_preads);
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test3219.cc b/storage/tokudb/PerconaFT/src/tests/test3219.cc
new file mode 100644
index 00000000000..99d1a90a775
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test3219.cc
@@ -0,0 +1,207 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+
+// This test, when run under helgrind, should detect the race problem documented in #3219.
+// The test:
+// checkpointing runs (in one thread)
+// another thread does an ft lookup.
+// We expect to see a lock-acquisition error.
+
+
+#include "test.h"
+#include <pthread.h>
+
+
+static DB_ENV *env;
+static DB *db;
+
+static void
+insert(int i, DB_TXN *txn)
+{
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ DBT key, val;
+ int r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&val, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void
+lookup(int i, DB_TXN *txn)
+// Do a lookup, but don't complain if it's not there.
+{
+ char hello[30], there[30], expectthere[30];
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(expectthere, sizeof(expectthere), "there%d", i);
+ DBT key, val;
+ val.data = there;
+ val.ulen = sizeof there;
+ val.flags = DB_DBT_USERMEM;
+ int r=db->get(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &val,
+ 0);
+ if (r==0) {
+ assert(val.data==there);
+ assert(val.size==strlen(expectthere)+1);
+ //printf("Found %s, expected %s\n", there, expectthere);
+ assert(strcmp(there, expectthere)==0);
+ }
+}
+
+#define N_ROWS 1000000
+#define N_TXNS 10000
+#define N_ROWS_PER_TXN 1
+
+#define INITIAL_SIZE 1000
+//#define N_TXNS 10
+//#define PER_TXN 10000
+
+static void
+setup (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_cachesize(env, 0, 128*1024, 1); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 4096); CKERR(r);
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<INITIAL_SIZE; i++) insert(random()%N_ROWS, txn);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+}
+
+
+static void
+finish (void) {
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+volatile int finished = false;
+
+// Thread A performs checkpoints
+static void*
+start_a (void *arg __attribute__((__unused__))) {
+ //r=env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ while (!finished) {
+ int r;
+ r=env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ sleep(1);
+ }
+ return NULL;
+}
+
+// Thread B performs insertions (eventually they start overwriting the same record).
+static void*
+start_b (void *arg __attribute__((__unused__))) {
+ int r;
+ for (int j=0; j<N_TXNS; j++) {
+ if (verbose) {
+ printf("."); fflush(stdout);
+ if (j%(N_TXNS/10)==0) printf("\n");
+ }
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<N_ROWS_PER_TXN; i++) {
+ insert(random()%N_ROWS, txn);
+ }
+ r = txn->commit(txn, DB_TXN_NOSYNC); CKERR(r);
+ }
+ finished = true;
+ return NULL;
+}
+
+// Thread C performs lookups
+static void*
+start_c (void *arg __attribute__((__unused__))) {
+ int r;
+ while (!finished) {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ lookup(random()%N_ROWS, txn);
+ r = txn->commit(txn, DB_TXN_NOSYNC); CKERR(r);
+ }
+ return NULL;
+}
+
+
+typedef void *(*pthread_fun)(void*);
+
+static void
+run_test (void)
+{
+ setup();
+ pthread_t t[3];
+ pthread_fun funs[3] = {start_a, start_b, start_c};
+ finished = false;
+ for (int i=0; i<3; i++) {
+ int r = pthread_create(&t[i], NULL, funs[i], NULL);
+ assert(r==0);
+ }
+ for (int i=0; i<3; i++) {
+ void *rv;
+ int r = pthread_join(t[i], &rv);
+ assert(r==0 && rv==NULL);
+ }
+ finish();
+}
+
+int test_main (int argc, char*const argv[]) {
+ parse_args(argc, argv);
+ run_test();
+ if (verbose) printf("\n");
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test3522.cc b/storage/tokudb/PerconaFT/src/tests/test3522.cc
new file mode 100644
index 00000000000..be594bd4c69
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test3522.cc
@@ -0,0 +1,178 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Test for #3522. Demonstrate that with DB_TRYAGAIN a cursor can stall.
+ * Strategy: Create a tree (with relatively small nodes so things happen quickly, and relatively large compared to the cache).
+ * In a single transaction: Delete everything, and then do a DB_FIRST.
+ * Make the test terminate by capturing the calls to pread(). */
+
+#include "test.h"
+#include <portability/toku_atomic.h>
+
+static DB_ENV *env;
+static DB *db;
+const int N = 1000;
+
+const int n_preads_limit = 1000;
+long n_preads = 0;
+
+static ssize_t my_pread (int fd, void *buf, size_t count, off_t offset) {
+ long n_read_so_far = toku_sync_fetch_and_add(&n_preads, 1);
+ if (n_read_so_far > n_preads_limit) {
+ if (verbose) fprintf(stderr, "Apparent infinite loop detected\n");
+ abort();
+ }
+ return pread(fd, buf, count, offset);
+}
+
+static void
+insert(int i, DB_TXN *txn)
+{
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ DBT key, val;
+ int r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&val, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void op_delete (int i, DB_TXN *x) {
+ char hello[30];
+ DBT key;
+ if (verbose>1) printf("op_delete %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ int r = db->del(db, x,
+ dbt_init(&key, hello, strlen(hello)+1),
+ 0);
+ CKERR(r);
+}
+
+static void
+setup (void) {
+ db_env_set_func_pread(my_pread);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_cachesize(env, 0, 128*1024, 1); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 4096); CKERR(r);
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<N; i++) insert(i, txn);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+}
+
+static void finish (void) {
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+int did_nothing = 0;
+
+static int
+do_nothing(DBT const *UU(a), DBT const *UU(b), void *UU(c)) {
+ did_nothing++;
+ return 0;
+}
+static void run_del_next (void) {
+ DB_TXN *txn;
+ DBC *cursor;
+ int r;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<N; i++) op_delete(i, txn);
+
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ if (verbose) printf("read_next\n");
+ n_preads = 0;
+ r = cursor->c_getf_next(cursor, 0, do_nothing, NULL); CKERR2(r, DB_NOTFOUND);
+ assert(did_nothing==0);
+ if (verbose) printf("n_preads=%ld\n", n_preads);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+static void run_del_prev (void) {
+ DB_TXN *txn;
+ DBC *cursor;
+ int r;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<N; i++) op_delete(i, txn);
+
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ if (verbose) printf("read_prev\n");
+ n_preads = 0;
+ r = cursor->c_getf_prev(cursor, 0, do_nothing, NULL); CKERR2(r, DB_NOTFOUND);
+ assert(did_nothing==0);
+ if (verbose) printf("n_preads=%ld\n", n_preads);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+static void run_test (void) {
+ setup();
+ run_del_next();
+ finish();
+
+ setup();
+ run_del_prev();
+ finish();
+}
+int test_main (int argc, char*const argv[]) {
+ parse_args(argc, argv);
+ run_test();
+ printf("n_preads=%ld\n", n_preads);
+ return 0;
+}
+
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test3522b.cc b/storage/tokudb/PerconaFT/src/tests/test3522b.cc
new file mode 100644
index 00000000000..287d0a66a11
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test3522b.cc
@@ -0,0 +1,189 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Test for #3522. Demonstrate that with DB_TRYAGAIN a cursor can stall.
+ * Strategy: Create a tree (with relatively small nodes so things happen quickly, and relatively large compared to the cache).
+ * In a single transaction: Delete everything except the last one, and then do a DB_FIRST.
+ * (Compare to test3522.c which deletes everything including the last one.)
+ * Make the test terminate by capturing the calls to pread(). */
+
+#include "test.h"
+#include <portability/toku_atomic.h>
+
+static DB_ENV *env;
+static DB *db;
+const int N = 1000;
+
+const int n_preads_limit = 1000;
+long n_preads = 0;
+
+static ssize_t my_pread (int fd, void *buf, size_t count, off_t offset) {
+ long n_read_so_far = toku_sync_fetch_and_add(&n_preads, 1);
+ if (n_read_so_far > n_preads_limit) {
+ if (verbose) fprintf(stderr, "Apparent infinite loop detected\n");
+ abort();
+ }
+ return pread(fd, buf, count, offset);
+}
+
+static void
+insert(int i, DB_TXN *txn)
+{
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ DBT key, val;
+ int r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&val, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void op_delete (int i, DB_TXN *x) {
+ char hello[30];
+ DBT key;
+ if (verbose>1) printf("op_delete %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ int r = db->del(db, x,
+ dbt_init(&key, hello, strlen(hello)+1),
+ 0);
+ CKERR(r);
+}
+
+static void
+setup (void) {
+ db_env_set_func_pread(my_pread);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_cachesize(env, 0, 128*1024, 1); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 4096); CKERR(r);
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<N; i++) insert(i, txn);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+}
+
+static void finish (void) {
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+int did_nothing_count = 0;
+int expect_n = -1;
+
+static int
+do_nothing(DBT const *a, DBT const *b, void *c) {
+ did_nothing_count++;
+ assert(c==NULL);
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%d", expect_n);
+ snprintf(there, sizeof(there), "there%d", expect_n);
+ assert(strlen(hello)+1 == a->size);
+ assert(strlen(there)+1 == b->size);
+ assert(strcmp(hello, (char*)a->data)==0);
+ assert(strcmp(there, (char*)b->data)==0);
+ return 0;
+}
+static void run_del_next (void) {
+ DB_TXN *txn;
+ DBC *cursor;
+ int r;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<N-1; i++) op_delete(i, txn);
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ expect_n = N-1;
+ did_nothing_count = 0;
+ n_preads = 0;
+ if (verbose) printf("read_next\n");
+ r = cursor->c_getf_next(cursor, 0, do_nothing, NULL); CKERR(r);
+ assert(did_nothing_count==1);
+ if (verbose) printf("n_preads=%ld\n", n_preads);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+static void run_del_prev (void) {
+ DB_TXN *txn;
+ DBC *cursor;
+ int r;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=1; i<N; i++) op_delete(i, txn);
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ expect_n = 0;
+ did_nothing_count = 0;
+ if (verbose) printf("read_prev\n");
+ n_preads = 0;
+ r = cursor->c_getf_prev(cursor, 0, do_nothing, NULL); CKERR(r);
+ assert(did_nothing_count==1);
+ if (verbose) printf("n_preads=%ld\n", n_preads);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+static void run_test (void) {
+ setup();
+ run_del_next();
+ finish();
+
+ setup();
+ run_del_prev();
+ finish();
+}
+int test_main (int argc, char*const argv[]) {
+ parse_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test3529.cc b/storage/tokudb/PerconaFT/src/tests/test3529.cc
new file mode 100644
index 00000000000..34f67a806aa
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test3529.cc
@@ -0,0 +1,210 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Test for #3522. Show that even with DB_TRYAGAIN, isolation still works.
+ * Strategy:
+ * 1. Create a tree (with relatively small nodes so things happen quickly, and relatively large compared to the cache).
+ * 2. Start two transactions YY and XX.
+ * 3. Force XX to precede YY (e.g., XX reads the last row, and then YY deletes it, under MVCC).
+ * 4. YY, in a single transaction: deletes everything
+ * 5. YY does do a DB_FIRST.
+ * Set things up so that while YY is doing it's retries, XX inserts a row at the beginning.
+ * Make the test terminate by capturing the calls to pread(). */
+
+#include "test.h"
+#include <portability/toku_atomic.h>
+
+static DB_ENV *env;
+static DB *db;
+const int N = 1000;
+static DB_TXN *XX, *YY;
+
+long do_XX_on_pread = -1;
+const int n_preads_limit = 1000;
+long n_preads = 0;
+
+static void insert(int i, DB_TXN *txn);
+
+static ssize_t my_pread (int fd, void *buf, size_t count, off_t offset) {
+ long n_read_so_far = toku_sync_fetch_and_add(&n_preads, 1);
+ if (do_XX_on_pread==n_read_so_far && XX != NULL) {
+ // we're supposed to do the XX operation now. Insert a row.
+ printf("Did XX\n");
+ insert(0, XX);
+ }
+ if (n_read_so_far > n_preads_limit) {
+ if (verbose) fprintf(stderr, "Apparent infinite loop detected\n");
+ abort();
+ }
+ return pread(fd, buf, count, offset);
+}
+
+static void
+insert(int i, DB_TXN *txn)
+{
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%04d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ DBT key, val;
+ if (db) {
+ int r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&val, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+ }
+}
+
+static void op_delete (int i, DB_TXN *x) {
+ char hello[30];
+ DBT key;
+ if (verbose>1) printf("op_delete %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%04d", i);
+ int r = db->del(db, x,
+ dbt_init(&key, hello, strlen(hello)+1),
+ 0);
+ CKERR(r);
+}
+
+int did_nothing = 0;
+
+static int
+do_nothing(DBT const *UU(a), DBT const *UU(b), void *UU(c)) {
+ did_nothing++;
+ return 0;
+}
+
+
+static void
+setup (void) {
+ db = NULL;
+ db_env_set_func_pread(my_pread);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_cachesize(env, 0, 2*128*1024, 1); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 4096); CKERR(r);
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (int i=0; i<N; i++) insert(i+1, txn);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ r = env->txn_begin(env, 0, &XX, DB_TXN_SNAPSHOT); CKERR(r);
+ r = env->txn_begin(env, 0, &YY, DB_TXN_SNAPSHOT); CKERR(r);
+
+ // Force XX to preceed YY by making XX read something. (YY will op_delete everything in a moment).
+ {
+ DBC *cursor;
+ r = db->cursor(db, XX, &cursor, 0); CKERR(r);
+ did_nothing = 0;
+ //r = cursor->c_getf_next(cursor, 0, do_nothing, NULL); CKERR(r);
+ //assert(did_nothing==1);
+ did_nothing = 0;
+ r = cursor->c_close(cursor); CKERR(r);
+ }
+}
+
+static void finish (void) {
+ int r;
+ r = YY->commit(YY, 0); CKERR(r);
+ YY = NULL;
+ r = XX->commit(XX, 0); CKERR(r);
+ XX = NULL;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+static void run_del_next (void) {
+ DBC *cursor;
+ int r;
+ for (int i=0; i<N; i++) op_delete(i+1, YY);
+
+ r = db->cursor(db, YY, &cursor, 0); CKERR(r);
+ if (verbose) printf("read_next\n");
+ n_preads = 0;
+ do_XX_on_pread = 2;
+ printf("doing on %ld\n", do_XX_on_pread);
+ r = cursor->c_getf_next(cursor, 0, do_nothing, NULL); CKERR2(r, DB_NOTFOUND);
+ do_XX_on_pread = 0;
+ assert(did_nothing==0);
+ if (verbose) printf("n_preads=%ld\n", n_preads);
+ r = cursor->c_close(cursor); CKERR(r);
+}
+
+static void run_del_prev (void) {
+ DBC *cursor;
+ int r;
+ for (int i=0; i<N; i++) op_delete(i+1, YY);
+
+ r = db->cursor(db, YY, &cursor, 0); CKERR(r);
+ if (verbose) printf("read_prev\n");
+ n_preads = 0;
+ r = cursor->c_getf_prev(cursor, 0, do_nothing, NULL); CKERR2(r, DB_NOTFOUND);
+ assert(did_nothing==0);
+ if (verbose) printf("n_preads=%ld\n", n_preads);
+ r = cursor->c_close(cursor); CKERR(r);
+}
+
+static void run_test (void) {
+ setup();
+ run_del_next();
+ finish();
+
+ setup();
+ run_del_prev();
+ finish();
+}
+int test_main (int argc, char*const argv[]) {
+ parse_args(argc, argv);
+ run_test();
+ return 0;
+}
+
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test4573-logtrim.cc b/storage/tokudb/PerconaFT/src/tests/test4573-logtrim.cc
new file mode 100644
index 00000000000..337d724a89c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test4573-logtrim.cc
@@ -0,0 +1,121 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/wait.h>
+
+const int envflags = DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER;
+
+const int my_lg_max = 100;
+
+int test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ pid_t pid;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ const int N = 5;
+
+ if (0==(pid=fork())) {
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_lg_max(env, my_lg_max); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ for (int i=0; i<N; i++) {
+ DBT k,v;
+ r = db->put(db, txn, dbt_init(&k, &i, sizeof(i)), dbt_init(&v, &i, sizeof(i)), 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ for (int i=0; i<N; i+=2) {
+ DBT k;
+ r = db->del(db, txn, dbt_init(&k, &i, sizeof(i)), 0); CKERR(r);
+ r = env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+ exit(0);
+
+ }
+ {
+ int status;
+ pid_t pid2 = wait(&status);
+ assert(pid2==pid);
+ assert(WIFEXITED(status) && WEXITSTATUS(status)==0);
+ }
+ // Now run recovery to see what happens.
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ for (int i=0; i<N; i++) {
+ DBT k;
+ DBT v;
+ dbt_init(&v, NULL, 0);
+ r = db->get(db, txn, dbt_init(&k, &i, sizeof(i)), &v, 0);
+ if (i%2==1) {
+ assert(r==0);
+ //printf("Got %d\n", *(int*)v.data);
+ } else {
+ assert(r==DB_NOTFOUND);
+ }
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ //toku_os_recursive_delete(TOKU_TEST_FILENAME);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test5092.cc b/storage/tokudb/PerconaFT/src/tests/test5092.cc
new file mode 100644
index 00000000000..36fee5f6749
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test5092.cc
@@ -0,0 +1,81 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <sys/wait.h>
+
+static void clean_env (const char *envdir) {
+ const int len = strlen(envdir)+100;
+ char cmd[len];
+ snprintf(cmd, len, "rm -rf %s", envdir);
+ int r = system(cmd);
+ CKERR(r);
+ CKERR(toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO));
+}
+
+static void setup_env (DB_ENV **envp, const char *envdir) {
+ { int chk_r = db_env_create(envp, 0); CKERR(chk_r); }
+ (*envp)->set_errfile(*envp, stderr);
+ { int chk_r = (*envp)->set_redzone(*envp, 0); CKERR(chk_r); }
+ { int chk_r = (*envp)->open(*envp, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void setup_env_and_prepare (DB_ENV **envp, const char *envdir, bool commit) {
+ DB *db;
+ DB_TXN *txn;
+ clean_env(envdir);
+ setup_env(envp, envdir);
+ CKERR(db_create(&db, *envp, 0));
+ CKERR(db->open(db, NULL, "foo.db", 0, DB_BTREE, DB_CREATE | DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO));
+ CKERR((*envp)->txn_begin(*envp, 0, &txn, 0));
+ uint8_t gid[DB_GID_SIZE];
+ memset(gid, 0, DB_GID_SIZE);
+ gid[0]=42;
+ CKERR(txn->prepare(txn, gid, 0));
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ if (commit)
+ CKERR(txn->commit(txn, 0));
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+ DB_ENV *env;
+ setup_env_and_prepare(&env, TOKU_TEST_FILENAME, true);
+ { int chk_r = env ->close(env, 0); CKERR(chk_r); }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test938.cc b/storage/tokudb/PerconaFT/src/tests/test938.cc
new file mode 100644
index 00000000000..195b5b82bd8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test938.cc
@@ -0,0 +1,186 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+unsigned char N=5;
+
+static int
+fact(int n) {
+ if (n<=2) return n;
+ else return n*fact(n-1);
+}
+
+static void
+swapc (unsigned char *a, unsigned char *b) {
+ unsigned char tmp=*a;
+ *a=*b;
+ *b=tmp;
+}
+
+DB_ENV *env;
+DB *db;
+
+static void
+run (int choice) {
+ unsigned char v[N];
+ int i;
+ int r;
+ for (i=0; i<N; i++) {
+ v[i]=(unsigned char)(10*i);
+ }
+ for (i=0; i<N; i++) {
+ int nchoices=N-i;
+ swapc(&v[i], &v[i+choice%nchoices]);
+ choice=choice/nchoices;
+ }
+ if (0) {
+ for (i=0; i<N; i++) {
+ printf("%d ", v[i]);
+ }
+
+ printf("\n");
+ }
+ DB_TXN *txn;
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ DBT kdbt,vdbt;
+ char key[2]={25,(char)v[i]};
+ char val=v[i];
+ //printf("put %d %d\n", key, val);
+ r=db->put(db, txn, dbt_init(&kdbt, &key, 2), dbt_init(&vdbt, &val, 1), 0); CKERR(r);
+ }
+ r=txn->commit(txn, DB_TXN_NOSYNC); CKERR(r);
+ }
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBC *c;
+ r=db->cursor(db, txn, &c, 0); CKERR(r);
+ DBT kdbt,vdbt;
+ memset(&kdbt, 0, sizeof(kdbt));
+ memset(&vdbt, 0, sizeof(vdbt));
+ i=0;
+ while (0==(r=c->c_get(c, &kdbt, &vdbt, DB_NEXT))) {
+ //printf("Got %d %d\n", *(unsigned char*)kdbt.data, *(unsigned char*)vdbt.data);
+ i++;
+ kdbt.data=0;
+ vdbt.data=0;
+ }
+ CKERR2(r, DB_NOTFOUND);
+ //printf("i=%d N=%d\n", i, N);
+ assert(i==N);
+ r=c->c_close(c); CKERR(r);
+ r=txn->commit(txn, DB_TXN_NOSYNC); CKERR(r);
+ }
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBC *c;
+ r=db->cursor(db, txn, &c, 0); CKERR(r);
+ DBT kdbt,vdbt;
+ memset(&kdbt, 0, sizeof(kdbt));
+ memset(&vdbt, 0, sizeof(vdbt));
+ i=0;
+ while (0==(r=(c->c_get(c, &kdbt, &vdbt, DB_FIRST)))) {
+ i++;
+ r = db->del(db, txn, &kdbt, DB_DELETE_ANY);
+ CKERR(r);
+ }
+ assert(r==DB_NOTFOUND);
+ r=c->c_close(c); CKERR(r);
+ r=txn->commit(txn, DB_TXN_NOSYNC); CKERR(r);
+ }
+ return;
+#if 0
+ char v101=101, v102=102, v1=1, v2=2;
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBT k,v;
+ r=db->put(db, txn, dbt_init(&k, &v1, 1), dbt_init(&v, &v101, 1), 0); CKERR(r);
+ r=db->put(db, txn, dbt_init(&k, &v2, 1), dbt_init(&v, &v102, 1), 0); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBC *c;
+ r=db->cursor(db, txn, &c, 0); CKERR(r);
+ DBT k,v;
+ r=c->c_get(c, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_FIRST); CKERR(r);
+ assert(*(char*)k.data==v1); assert(*(char*)v.data==v101);
+ r=c->c_get(c, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_NEXT); CKERR(r);
+ assert(*(char*)k.data==v2); assert(*(char*)v.data==v102);
+ r=c->c_get(c, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_NEXT); assert(r!=0);
+ r=c->c_close(c); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ }
+#endif
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_TXN *txn;
+ {
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ }
+ int i;
+ //printf("fact(%d)=%d\n", N, fact(N));
+ for (i=0; i<fact(N); i++) {
+ run(i);
+ }
+ {
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test938b.cc b/storage/tokudb/PerconaFT/src/tests/test938b.cc
new file mode 100644
index 00000000000..6c9db74e5bf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test938b.cc
@@ -0,0 +1,113 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+DB *db;
+
+static void
+run (void) {
+ int r;
+ DB_TXN *txn;
+ char v101=101, v102=102, v1=1, v2=2;
+ int vN=0;
+ int N=0;
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBT k,v;
+ int i;
+ r=db->put(db, txn, dbt_init(&k, &v1, 1), dbt_init(&v, &v101, 1), 0); CKERR(r);
+ r=db->put(db, txn, dbt_init(&k, &v2, 1), dbt_init(&v, &v102, 1), 0); CKERR(r);
+ for (i=0; i<N; i++) {
+ int iv = htonl(i);
+ r=db->put(db, txn, dbt_init(&k, &vN, 1), dbt_init(&v, &iv, 4), 0); CKERR(r);
+ }
+ r=txn->commit(txn, 0); CKERR(r);
+ }
+
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBC *c;
+ r=db->cursor(db, txn, &c, 0); CKERR(r);
+ DBT k,v;
+ int i;
+ for (i=0; i<N; i++) {
+ r=c->c_get(c, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_NEXT); CKERR(r);
+ assert(k.size==1); assert(v.size==4);
+ assert(*(char*)k.data==vN); assert((int)ntohl(*(int*)v.data)==i);
+ }
+
+ r=c->c_get(c, dbt_init(&k, 0, 0), dbt_init(&v, 0, 0), DB_NEXT); CKERR(r);
+ assert(*(char*)k.data==v1); assert(*(char*)v.data==v101);
+ r=c->c_get(c, dbt_init(&k, 0, 0), dbt_init(&v, 0, 0), DB_NEXT); CKERR(r);
+ assert(*(char*)k.data==v2); assert(*(char*)v.data==v102);
+ r=c->c_get(c, dbt_init(&k, 0, 0), dbt_init(&v, 0, 0), DB_NEXT); assert(r!=0);
+ r=c->c_close(c); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ }
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_TXN *txn;
+ {
+ r = db_env_create(&env, 0); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ }
+ run();
+ {
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test938c.cc b/storage/tokudb/PerconaFT/src/tests/test938c.cc
new file mode 100644
index 00000000000..16c9a6d7f3a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test938c.cc
@@ -0,0 +1,120 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <db.h>
+#include <sys/stat.h>
+
+unsigned char N=5;
+
+DB_ENV *env;
+DB *db;
+
+static void
+run (void) {
+ int r;
+ DB_TXN *txn, *txn2;
+ char v101=101, v102=102, v1=1, v2=1;
+ // Add (1,102) to the tree
+ // In one txn
+ // add (1,101) to the tree
+ // In another concurrent txn
+ // look up (1,102) and do DB_NEXT
+ // That should be fine in PerconaFT.
+ // It fails before #938 is fixed.
+ // It also fails for BDB for other reasons (page-level locking vs. row-level locking)
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ char kk[2] = {v2, v102};
+ DBT k,v;
+ r=db->put(db, txn, dbt_init(&k, &kk, 2), dbt_init(&v, &v102, 1), 0); CKERR(r);
+
+ r=txn->commit(txn, 0); CKERR(r);
+ }
+ {
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn2, 0); CKERR(r);
+
+ DBT k,v;
+ {
+ char kk[2] = {v1, v101};
+ r=db->put(db, txn, dbt_init(&k, &kk, 2), dbt_init(&v, &v101, 1), 0); CKERR(r);
+ }
+
+ DBC *c2;
+ r=db->cursor(db, txn2, &c2, 0); CKERR(r);
+
+ {
+ char kk[2] = {v2, v102};
+ r=c2->c_get(c2, dbt_init(&k, &kk, 2), dbt_init(&v, &v102, 1), DB_SET); CKERR(r);
+ }
+ r=c2->c_get(c2, dbt_init_malloc(&k), dbt_init_malloc(&v), DB_NEXT); assert(r==DB_NOTFOUND);
+
+ r=c2->c_close(c2);
+ r=txn->commit(txn, 0); CKERR(r);
+ r=txn2->commit(txn2, 0); CKERR(r);
+ }
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_TXN *txn;
+ {
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ }
+ run();
+ {
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_3529_insert_2.cc b/storage/tokudb/PerconaFT/src/tests/test_3529_insert_2.cc
new file mode 100644
index 00000000000..b0fe4ab7aa1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_3529_insert_2.cc
@@ -0,0 +1,219 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that serializable cursor locks deleted keys so that another transaction can not insert into the range being scanned by the cursor
+// we create 2 level tree that looks like
+// root node with pivot key 2
+// left leaf contains keys 0, 1, and 2
+// right leaf contains keys 3 and 4
+// we delete key 2 while a snapshot txn exist so that garbage collection does not occur.
+// txn_a walks a cursor through the deleted keys.
+// when txn_a finishes reading the deleted keys, txn_b tries to insert key 2 and should get lock not granted.
+
+#include <db.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+static DB_ENV *env = NULL;
+static DB_TXN *txn_a = NULL;
+static DB_TXN *txn_b = NULL;
+static DB *db = NULL;
+static uint32_t db_page_size = 4096;
+// static uint32_t db_basement_size = 4096;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static int
+my_compare(DB *this_db UU(), const DBT *a UU(), const DBT *b UU()) {
+ assert(a->size == b->size);
+ return memcmp(a->data, b->data, a->size);
+}
+
+static int
+my_generate_row(DB *dest_db UU(), DB *src_db UU(), DBT_ARRAY *dest_key_arrays UU(), DBT_ARRAY *dest_val_arrays UU(), const DBT *src_key UU(), const DBT *src_val UU()) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ assert(dest_key->flags == DB_DBT_REALLOC);
+ dest_key->data = toku_realloc(dest_key->data, src_key->size);
+ memcpy(dest_key->data, src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ assert(dest_val->flags == DB_DBT_REALLOC);
+ dest_val->data = toku_realloc(dest_val->data, src_val->size);
+ memcpy(dest_val->data, src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+static int
+next_do_nothing(DBT const *UU(a), DBT const *UU(b), void *UU(c)) {
+ return 0;
+}
+
+static void *
+do_insert_2(void *arg) {
+ int r;
+ uint64_t key = 2;
+ char val[800]; memset(val, 0, sizeof val);
+ DBT k,v;
+ r = db->put(db, txn_b, dbt_init(&k, &key, sizeof key), dbt_init(&v, val, sizeof val), 0);
+ assert(r == DB_LOCK_NOTGRANTED);
+ return arg;
+}
+
+static ssize_t
+my_pread (int fd, void *buf, size_t count, off_t offset) {
+ static int my_pread_count = 0;
+ if (++my_pread_count == 5) {
+ pthread_t id;
+ pthread_create(&id, NULL, do_insert_2, NULL);
+ void *ret;
+ pthread_join(id, &ret);
+ }
+ return pread(fd, buf, count, offset);
+}
+
+static void
+run_test(void) {
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, my_generate_row); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, db_page_size);
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // build a tree with 2 leaf nodes
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DB_LOADER *loader = NULL;
+ r = env->create_loader(env, txn, &loader, db, 1, &db, NULL, NULL, 0); CKERR(r);
+ for (uint64_t i = 0; i < 5; i++) {
+ uint64_t key = i;
+ char val[800]; memset(val, 0, sizeof val);
+ DBT k,v;
+ r = loader->put(loader, dbt_init(&k, &key, sizeof key), dbt_init(&v, val, sizeof val)); CKERR(r);
+ }
+ r = loader->close(loader); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // delete key 2
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (uint64_t i = 2; i < 3; i++) {
+ uint64_t key = i;
+ DBT k;
+ r = db->del(db, txn, dbt_init(&k, &key, sizeof key), 0); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // close and reopen
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // create a txn that will try to insert key 2 while the serializable cursor is walking through the tree
+ r = env->txn_begin(env, 0, &txn_b, 0); CKERR(r);
+
+ // walk a serializable cursor through the tree
+ r = env->txn_begin(env, 0, &txn_a, 0); CKERR(r);
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn_a, &cursor, 0); CKERR(r);
+ db_env_set_func_pread(my_pread);
+ while (1) {
+ r = cursor->c_getf_next(cursor, 0, next_do_nothing, NULL);
+ if (r != 0)
+ break;
+ }
+ db_env_set_func_pread(NULL);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn_a->commit(txn_a, 0); CKERR(r);
+
+ r = txn_b->commit(txn_b, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static int
+usage(void) {
+ fprintf(stderr, "-v (verbose)\n");
+ fprintf(stderr, "-q (quiet)\n");
+ fprintf(stderr, "--envdir %s\n", envdir);
+ return 1;
+}
+
+int
+test_main (int argc , char * const argv[]) {
+ for (int i = 1 ; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--envdir") == 0 && i+1 < argc) {
+ envdir = argv[++i];
+ continue;
+ }
+ return usage();
+ }
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ int r;
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ run_test();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc b/storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc
new file mode 100644
index 00000000000..2d4e9ba14ea
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc
@@ -0,0 +1,212 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that serializable cursor locks deleted keys so that another transaction can not insert into the range being scanned by the cursor
+// we create 2 level tree that looks like
+// root node with pivot key 2
+// left leaf contains keys 0, 1, and 2
+// right leaf contains keys 3 and 4
+// we delete keys 0, 1, and 2 while a snapshot txn exist so that garbage collection does not occur.
+// txn_a walks a cursor through the deleted keys.
+// when txn_a finishes reading the deleted keys, txn_b tries to get a table lock.
+// the table lock should fail since txn_a holds a read lock on the deleted key range.
+
+#include <db.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static DB_ENV *env = NULL;
+static DB_TXN *txn_a = NULL;
+static DB_TXN *txn_b = NULL;
+static DB *db = NULL;
+static uint32_t db_page_size = 4096;
+// static uint32_t db_basement_size = 4096;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static int
+my_compare(DB *this_db UU(), const DBT *a UU(), const DBT *b UU()) {
+ assert(a->size == b->size);
+ return memcmp(a->data, b->data, a->size);
+}
+
+static int
+my_generate_row(DB *dest_db UU(), DB *src_db UU(), DBT_ARRAY *dest_key_arrays UU(), DBT_ARRAY *dest_val_arrays UU(), const DBT *src_key UU(), const DBT *src_val UU()) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ assert(dest_key->flags == DB_DBT_REALLOC);
+ dest_key->data = toku_realloc(dest_key->data, src_key->size);
+ memcpy(dest_key->data, src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ assert(dest_val->flags == DB_DBT_REALLOC);
+ dest_val->data = toku_realloc(dest_val->data, src_val->size);
+ memcpy(dest_val->data, src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+static int
+next_do_nothing(DBT const *UU(a), DBT const *UU(b), void *UU(c)) {
+ return 0;
+}
+
+static ssize_t
+my_pread (int fd, void *buf, size_t count, off_t offset) {
+ static int my_pread_count = 0;
+ if (++my_pread_count == 5) {
+ // try to acquire a table lock, should fail
+ int r = db->pre_acquire_table_lock(db, txn_b);
+ assert(r == DB_LOCK_NOTGRANTED);
+ }
+ return pread(fd, buf, count, offset);
+}
+
+static void
+run_test(void) {
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, my_generate_row); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, db_page_size);
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // build a tree with 2 leaf nodes
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DB_LOADER *loader = NULL;
+ r = env->create_loader(env, txn, &loader, db, 1, &db, NULL, NULL, 0); CKERR(r);
+ for (uint64_t i = 0; i < 5; i++) {
+ uint64_t key = i;
+ char val[800]; memset(val, 0, sizeof val);
+ DBT k,v;
+ r = loader->put(loader, dbt_init(&k, &key, sizeof key), dbt_init(&v, val, sizeof val)); CKERR(r);
+ }
+ r = loader->close(loader); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // this transaction ensure that garbage collection does not occur when deleting
+ DB_TXN *bogus_txn = NULL;
+ r = env->txn_begin(env, 0, &bogus_txn, DB_TXN_SNAPSHOT); CKERR(r);
+
+ // delete the keys in the first leaf node
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (uint64_t i = 0; i < 3; i++) {
+ uint64_t key = i;
+ DBT k;
+ r = db->del(db, txn, dbt_init(&k, &key, sizeof key), 0); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+ r = bogus_txn->commit(bogus_txn, 0); CKERR(r);
+
+ // close and reopen
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // create a txn that will try to acquire a write lock on key 0 in the pread callback
+ r = env->txn_begin(env, 0, &txn_b, 0); CKERR(r);
+
+ // walk a serializable cursor through the tree
+ r = env->txn_begin(env, 0, &txn_a, 0); CKERR(r);
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn_a, &cursor, 0); CKERR(r);
+ db_env_set_func_pread(my_pread);
+ while (1) {
+ r = cursor->c_getf_next(cursor, 0, next_do_nothing, NULL);
+ if (r != 0)
+ break;
+ }
+ db_env_set_func_pread(NULL);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn_a->commit(txn_a, 0); CKERR(r);
+
+ r = txn_b->commit(txn_b, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static int
+usage(void) {
+ fprintf(stderr, "-v (verbose)\n");
+ fprintf(stderr, "-q (quiet)\n");
+ fprintf(stderr, "--envdir %s\n", envdir);
+ return 1;
+}
+
+int
+test_main (int argc , char * const argv[]) {
+ for (int i = 1 ; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--envdir") == 0 && i+1 < argc) {
+ envdir = argv[++i];
+ continue;
+ }
+ return usage();
+ }
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ int r;
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ run_test();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_3645.cc b/storage/tokudb/PerconaFT/src/tests/test_3645.cc
new file mode 100644
index 00000000000..bb5566b032d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_3645.cc
@@ -0,0 +1,322 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+//
+// This test verifies that running evictions on a writer thread
+// are ok. We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - update existing values in the dictionary with db->put(DB_YESOVERWRITE)
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success.
+//
+
+bool run_test;
+int time_of_test;
+int num_elements;
+
+struct arg {
+ int n;
+ DB *db;
+ DB_ENV* env;
+ bool fast;
+ bool fwd;
+};
+
+static int
+go_fast(DBT const *a, DBT const *b, void *c) {
+ assert(a);
+ assert(b);
+ assert(c==NULL);
+ return TOKUDB_CURSOR_CONTINUE;
+}
+static int
+go_slow(DBT const *a, DBT const *b, void *c) {
+ assert(a);
+ assert(b);
+ assert(c==NULL);
+ return 0;
+}
+
+static void *scan_db(void *arg) {
+ struct arg *myarg = (struct arg *) arg;
+ DB_ENV* env = myarg->env;
+ DB* db = myarg->db;
+ DB_TXN* txn = NULL;
+ while(run_test) {
+ int r = env->txn_begin(env, 0, &txn, DB_TXN_SNAPSHOT); CKERR(r);
+ DBC* cursor = NULL;
+ { int chk_r = db->cursor(db, txn, &cursor, 0); CKERR(chk_r); }
+ while (r != DB_NOTFOUND) {
+ if (myarg->fwd) {
+ r = cursor->c_getf_next(cursor, 0, myarg->fast ? go_fast : go_slow, NULL);
+ }
+ else {
+ r = cursor->c_getf_prev(cursor, 0, myarg->fast ? go_fast : go_slow, NULL);
+ }
+ assert(r==0 || r==DB_NOTFOUND);
+ }
+
+ { int chk_r = cursor->c_close(cursor); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+ }
+ return arg;
+}
+
+static void *ptquery_db(void *arg) {
+ struct arg *myarg = (struct arg *) arg;
+ DB_ENV* env = myarg->env;
+ DB* db = myarg->db;
+ DB_TXN* txn = NULL;
+ int n = myarg->n;
+ while(run_test) {
+ int r = env->txn_begin(env, 0, &txn, DB_TXN_SNAPSHOT); CKERR(r);
+ int rand_key = random() % n;
+ DBT key;
+ DBT val;
+ memset(&val, 0, sizeof(val));
+ dbt_init(&key, &rand_key, sizeof(rand_key));
+ r = db->get(db, txn, &key, &val, 0);
+ assert(r != DB_NOTFOUND);
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+ }
+ return arg;
+}
+
+static void *update_db(void *arg) {
+ struct arg *myarg = (struct arg *) arg;
+ DB_ENV* env = myarg->env;
+ DB* db = myarg->db;
+ int n = myarg->n;
+
+ DB_TXN* txn = NULL;
+ while (run_test) {
+ int r = env->txn_begin(env, 0, &txn, DB_TXN_SNAPSHOT); CKERR(r);
+ for (uint32_t i = 0; i < 1000; i++) {
+ int rand_key = random() % n;
+ int rand_val = random();
+ DBT key, val;
+ r = db->put(
+ db,
+ txn,
+ dbt_init(&key, &rand_key, sizeof(rand_key)),
+ dbt_init(&val, &rand_val, sizeof(rand_val)),
+ 0
+ );
+ CKERR(r);
+ }
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+ }
+ return arg;
+}
+
+static void *test_time(void *arg) {
+ assert(arg == NULL);
+ usleep(time_of_test*1000*1000);
+ if (verbose) printf("should now end test\n");
+ run_test = false;
+ return arg;
+}
+
+
+static void
+test_evictions (void) {
+ int n = num_elements;
+ if (verbose) printf("test_3645:%d \n", n);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.bulk_fetch.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r=env->set_default_bt_compare(env, int_dbt_cmp); CKERR(r);
+ // set the cache size to 10MB
+ r = env->set_cachesize(env, 0, 100000, 1); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->checkpointing_set_period(env, 10);
+ CKERR(r);
+
+
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_flags(db, 0);
+ assert(r == 0);
+ r = db->set_pagesize(db, 4096);
+ assert(r == 0);
+ r = db->set_readpagesize(db, 1024);
+ assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ int keys[n];
+ for (int i=0; i<n; i++) {
+ keys[i] = i;
+ }
+
+ if (verbose) printf("starting insertion of elements to setup test\n");
+ for (int i=0; i<n; i++) {
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &keys[i], sizeof keys[i]), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ //
+ // the threads that we want:
+ // - one thread constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - one thread doing random point queries
+ //
+ run_test = true;
+ if (verbose) printf("starting creation of pthreads\n");
+ toku_pthread_t mytids[7];
+ struct arg myargs[7];
+ for (uint32_t i = 0; i < sizeof(myargs)/sizeof(myargs[0]); i++) {
+ myargs[i].n = n;
+ myargs[i].db = db;
+ myargs[i].env = env;
+ myargs[i].fast = true;
+ myargs[i].fwd = true;
+ }
+
+ // make the forward fast scanner
+ myargs[0].fast = true;
+ myargs[0].fwd = true;
+ { int chk_r = toku_pthread_create(&mytids[0], NULL, scan_db, &myargs[0]); CKERR(chk_r); }
+
+ // make the forward slow scanner
+ myargs[1].fast = false;
+ myargs[1].fwd = true;
+ { int chk_r = toku_pthread_create(&mytids[1], NULL, scan_db, &myargs[1]); CKERR(chk_r); }
+
+ // make the backward fast scanner
+ myargs[2].fast = true;
+ myargs[2].fwd = false;
+ { int chk_r = toku_pthread_create(&mytids[2], NULL, scan_db, &myargs[2]); CKERR(chk_r); }
+
+ // make the backward slow scanner
+ myargs[3].fast = false;
+ myargs[3].fwd = false;
+ { int chk_r = toku_pthread_create(&mytids[3], NULL, scan_db, &myargs[3]); CKERR(chk_r); }
+
+ // make the guy that updates the db
+ { int chk_r = toku_pthread_create(&mytids[4], NULL, update_db, &myargs[4]); CKERR(chk_r); }
+
+ // make the guy that does point queries
+ { int chk_r = toku_pthread_create(&mytids[5], NULL, ptquery_db, &myargs[5]); CKERR(chk_r); }
+
+ // make the guy that sleeps
+ { int chk_r = toku_pthread_create(&mytids[6], NULL, test_time, NULL); CKERR(chk_r); }
+
+ for (uint32_t i = 0; i < sizeof(myargs)/sizeof(myargs[0]); i++) {
+ void *ret;
+ r = toku_pthread_join(mytids[i], &ret); assert_zero(r);
+ }
+ if (verbose) printf("ending test, pthreads have joined\n");
+
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static inline void parse_3645_args (int argc, char *const argv[]) {
+ const char *argv0=argv[0];
+ while (argc>1) {
+ int resultcode=0;
+ if (strcmp(argv[1], "-v")==0) {
+ verbose++;
+ }
+ else if (strcmp(argv[1], "-q")==0) {
+ verbose=0;
+ }
+ else if (strcmp(argv[1], "-h")==0) {
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-h-q|--num_elements number | --num_seconds number]\n", argv0);
+ exit(resultcode);
+ }
+ else if (strcmp(argv[1], "--num_elements") == 0) {
+ argc--;
+ argv++;
+ num_elements = atoi(argv[1]);
+ }
+ else if (strcmp(argv[1], "--num_seconds") == 0) {
+ argc--;
+ argv++;
+ time_of_test = atoi(argv[1]);
+ }
+ else {
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ // default values
+ num_elements = 100000;
+ time_of_test = 60;
+ parse_3645_args(argc, argv);
+ test_evictions();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_3755.cc b/storage/tokudb/PerconaFT/src/tests/test_3755.cc
new file mode 100644
index 00000000000..67ef3b4b738
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_3755.cc
@@ -0,0 +1,156 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// stress test for update broadcast. 10M 8-byte keys should be 2, maybe 3
+// levels of treeness, makes sure flushes work
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = 1024;
+
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ assert(extra->size == sizeof(unsigned int));
+ assert(old_val->size == sizeof(unsigned int));
+ unsigned int e = *(unsigned int *)extra->data;
+ unsigned int ov = *(unsigned int *)old_val->data;
+ assert(e == (ov+1));
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &e, sizeof(e)), set_extra);
+ }
+ //usleep(10);
+ return 0;
+}
+
+static int
+int_cmp(DB *UU(db), const DBT *a, const DBT *b) {
+ unsigned int *ap, *bp;
+ assert(a->size == sizeof(*ap));
+ CAST_FROM_VOIDP(ap, a->data);
+ assert(b->size == sizeof(*bp));
+ CAST_FROM_VOIDP(bp, b->data);
+ return (*ap > *bp) - (*ap < *bp);
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->set_default_bt_compare(env, int_cmp); CKERR(chk_r); }
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ // make a really small checkpointing period
+ { int chk_r = env->checkpointing_set_period(env,1); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = 0;
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db, unsigned int i) {
+ DBT extra;
+ unsigned int e = i;
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ int r = db->update_broadcast(db, txn, extrap, 0); CKERR(r);
+ return r;
+}
+
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->set_pagesize(db, 1<<8); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ for(unsigned int i = 1; i < 100; i++) {
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, i); CKERR(chk_r); }
+ });
+ for (unsigned int curr_key = 0; curr_key < NUM_KEYS; ++curr_key) {
+ DBT key, val;
+ unsigned int *vp;
+ DBT *keyp = dbt_init(&key, &curr_key, sizeof(curr_key));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db->get(db, txn_3, keyp, valp, 0); CKERR(chk_r); }
+ });
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp==i);
+ }
+ }
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_4015.cc b/storage/tokudb/PerconaFT/src/tests/test_4015.cc
new file mode 100644
index 00000000000..dc18242b489
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_4015.cc
@@ -0,0 +1,171 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <portability/toku_atomic.h>
+
+static int my_compare (DB *db, const DBT *a, const DBT *b) {
+ assert(db);
+ assert(db->cmp_descriptor);
+ assert(db->cmp_descriptor->dbt.size >= 3);
+ char *CAST_FROM_VOIDP(data, db->cmp_descriptor->dbt.data);
+ assert(data[0]=='f');
+ assert(data[1]=='o');
+ assert(data[2]=='o');
+ if (verbose) printf("compare descriptor=%s\n", data);
+ sched_yield();
+ return uint_dbt_cmp(db, a, b);
+}
+
+DB_ENV *env;
+DB *db;
+const char *env_dir = TOKU_TEST_FILENAME;
+volatile int done = 0;
+
+static void *startA (void *ignore __attribute__((__unused__))) {
+ for (int i=0;i<999; i++) {
+ DBT k,v;
+ int a = (random()<<16) + i;
+ dbt_init(&k, &a, sizeof(a));
+ dbt_init(&v, &a, sizeof(a));
+ DB_TXN *txn;
+ again:
+ { int chk_r = env->txn_begin(env, NULL, &txn, DB_TXN_NOSYNC); CKERR(chk_r); }
+ {
+ int r = db->put(db, txn, &k, &v, 0);
+ if (r==DB_LOCK_NOTGRANTED) {
+ if (verbose) printf("lock not granted on %d\n", i);
+ { int chk_r = txn->abort(txn); CKERR(chk_r); }
+ goto again;
+ }
+ assert(r==0);
+ }
+ { int chk_r = txn->commit(txn, 0); CKERR(chk_r); }
+ }
+ int r __attribute__((__unused__)) = toku_sync_fetch_and_add(&done, 1);
+ return NULL;
+}
+static void change_descriptor (DB_TXN *txn, int i) {
+ DBT desc;
+ char foo[100];
+ snprintf(foo, 99, "foo%d", i);
+ dbt_init(&desc, foo, 1+strlen(foo));
+ int r;
+ if (verbose) printf("trying to change to %s\n", foo);
+ while ((r=db->change_descriptor(db, txn, &desc, 0))) {
+ if (verbose) printf("Change failed r=%d, try again\n", r);
+ }
+ if (verbose) printf("ok\n");
+}
+static void startB (void) {
+ for (int i=0; !done; i++) {
+ IN_TXN_COMMIT(env, NULL, txn, 0,
+ change_descriptor(txn, i));
+ sched_yield();
+ }
+}
+
+static void my_parse_args (int argc, char * const argv[]) {
+ const char *argv0=argv[0];
+ while (argc>1) {
+ int resultcode=0;
+ if (strcmp(argv[1], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[1],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[1],"--envdir")==0) {
+ assert(argc>2);
+ env_dir = argv[2];
+ argc--;
+ argv++;
+ } else if (strcmp(argv[1], "-h")==0) {
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q] [-h] [--envdir <envdir>]\n", argv0);
+ exit(resultcode);
+ } else {
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+int test_main(int argc, char * const argv[]) {
+ my_parse_args(argc, argv);
+
+ db_env_set_num_bucket_mutexes(32);
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ { int chk_r = env->set_redzone(env, 0); CKERR(chk_r); }
+ { int chk_r = env->set_default_bt_compare(env, my_compare); CKERR(chk_r); }
+ {
+ const int size = 10+strlen(env_dir);
+ char cmd[size];
+ snprintf(cmd, size, "rm -rf %s", env_dir);
+ int r = system(cmd);
+ CKERR(r);
+ }
+ { int chk_r = toku_os_mkdir(env_dir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+ { int chk_r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->set_pagesize(db, 1024); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ IN_TXN_COMMIT(env, NULL, txn, 0,
+ { int chk_r = db->change_descriptor(db, txn, &desc, DB_UPDATE_CMP_DESCRIPTOR); CKERR(chk_r); });
+ pthread_t thd;
+ { int chk_r = toku_pthread_create(&thd, NULL, startA, NULL); CKERR(chk_r); }
+
+ startB();
+
+ void *retval;
+ { int chk_r = toku_pthread_join(thd, &retval); CKERR(chk_r); }
+ assert(retval==NULL);
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_4368.cc b/storage/tokudb/PerconaFT/src/tests/test_4368.cc
new file mode 100644
index 00000000000..21a3050f14e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_4368.cc
@@ -0,0 +1,71 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Can I close a db without opening it? */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ DB_ENV *env;
+ DB *db;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_create(&db, env, 0); assert(r==0);
+ r = db->open(db, NULL, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+
+ // call hot_optimize on an empty db. The empty db should have only a root node, which should invoke the bug
+ uint64_t loops_run;
+ r = db->hot_optimize(db, NULL, NULL, NULL, NULL, &loops_run); assert_zero(r);
+
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_4657.cc b/storage/tokudb/PerconaFT/src/tests/test_4657.cc
new file mode 100644
index 00000000000..70c9eb16189
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_4657.cc
@@ -0,0 +1,133 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+//
+// test that nodes written out for checkpointing properly update the stats
+// insert a bunch of elements, but not too many. The amount of data should
+// fit into a single leaf node. Then we :
+// - checkpoint
+// - close dictionary
+// - reopen dictionary
+// - call stat64
+// prior to the fix for 4657, the stats would return
+// 0 rows. After the fix, the stats should return an
+// accurate number of rows
+//
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ DB *db;
+ {
+ DB_TXN *txna;
+ r = env->txn_begin(env, NULL, &txna, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ CKERR(r);
+ r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ r = txna->commit(txna, 0); CKERR(r);
+ }
+ if (verbose) printf("starting insertion of even elements\n");
+ //
+ // now insert 1000 elements
+ //
+ DB_TXN* txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ for (uint32_t i = 0; i < 1000; i++) {
+ DBT key,val;
+ uint64_t key_data = i;
+ uint64_t val_data = i;
+ r = db->put(
+ db,
+ txn,
+ dbt_init(&key, &key_data, sizeof(key_data)),
+ dbt_init(&val, &val_data, sizeof(val_data)),
+ 0
+ );
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ //
+ // the assumption here is that the db consists
+ // of a single leaf node that is the root.
+ //
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+
+ // now reopen
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_THREAD, 0666);
+ CKERR(r);
+ DB_BTREE_STAT64 dict_stats;
+ r = db->stat64(
+ db,
+ NULL,
+ &dict_stats
+ );
+ CKERR(r);
+ // check that stats are correct
+ assert(dict_stats.bt_nkeys == 1000);
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_5015.cc b/storage/tokudb/PerconaFT/src/tests/test_5015.cc
new file mode 100644
index 00000000000..619a93f3092
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_5015.cc
@@ -0,0 +1,99 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <db.h>
+#include <errno.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.already.exists.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ DB_TXN* parent_txn = NULL;
+ DB_TXN* child_txn = NULL;
+ r = env->txn_begin(env, 0, &parent_txn, 0);
+ CKERR(r);
+ r = env->txn_begin(env, parent_txn, &child_txn, 0);
+ CKERR(r);
+ DBT key,val;
+ r = db->put(db, child_txn, dbt_init(&key, "a", 2), dbt_init(&val, "a", 2), 0);
+ CKERR(r);
+ uint8_t gid[DB_GID_SIZE];
+ memset(gid, 0, DB_GID_SIZE);
+ gid[0]='a';
+ r = child_txn->prepare(child_txn, gid, 0);
+ CKERR(r);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ r = child_txn->commit(child_txn, 0);
+ CKERR(r);
+ r = parent_txn->commit(parent_txn, 0);
+ CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_5469.cc b/storage/tokudb/PerconaFT/src/tests/test_5469.cc
new file mode 100644
index 00000000000..5e58057a385
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_5469.cc
@@ -0,0 +1,172 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static int put_multiple_generate(DB *UU(dest_db), DB *UU(src_db), DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+ return 0;
+}
+
+static void
+test_loader_abort (bool do_compress, bool abort_loader, bool abort_txn) {
+ DB_ENV * env;
+ DB *db;
+ DB_TXN *txn;
+ DB_TXN* const null_txn = 0;
+ const char * const fname = "test.loader_abort.ft_handle";
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_LOADER *loader;
+ uint32_t db_flags = 0;
+ uint32_t dbt_flags = 0;
+ uint32_t loader_flags = do_compress ? LOADER_COMPRESS_INTERMEDIATES : 0;
+ DBC* cursor = NULL;
+
+ /* create the dup database file */
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->set_generate_row_callback_for_put(env, put_multiple_generate);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,stderr); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ r = env->create_loader(env, txn, &loader, db, 1, &db, &db_flags, &dbt_flags, loader_flags);
+ CKERR(r);
+
+ DBT key, val;
+ uint32_t k;
+ uint32_t v;
+ uint32_t num_elements = 2;
+ for (uint32_t i = 0; i < num_elements; i++) {
+ k = i;
+ v = i;
+ r = loader->put(
+ loader,
+ dbt_init(&key, &k, sizeof k),
+ dbt_init(&val, &v, sizeof v)
+ );
+ assert(r == 0);
+ }
+ if (abort_loader) {
+ loader->abort(loader);
+ }
+ else {
+ loader->close(loader);
+ }
+ k = num_elements;
+ v = num_elements;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+
+ if (abort_txn) {
+ r = txn->abort(txn);
+ CKERR(r);
+ }
+ else {
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ DBT k1; memset(&k1, 0, sizeof k1);
+ DBT v1; memset(&v1, 0, sizeof v1);
+ if (!abort_txn) {
+ if (!abort_loader) {
+ for (uint32_t i = 0; i < num_elements; i++) {
+ r = cursor->c_get(cursor, &k1, &v1, DB_NEXT); assert(r == 0);
+ assert(k1.size == sizeof(uint32_t));
+ assert(v1.size == sizeof(uint32_t));
+ assert(*(uint32_t *)k1.data == i);
+ assert(*(uint32_t *)v1.data == i);
+ }
+ }
+ r = cursor->c_get(cursor, &k1, &v1, DB_NEXT); assert(r == 0);
+ assert(k1.size == sizeof(uint32_t));
+ assert(v1.size == sizeof(uint32_t));
+ assert(*(uint32_t *)k1.data == num_elements);
+ assert(*(uint32_t *)v1.data == num_elements);
+ }
+ r = cursor->c_get(cursor, &k1, &v1, DB_NEXT); assert(r == DB_NOTFOUND);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_loader_abort(false, false, true);
+ test_loader_abort(false, true, true);
+ test_loader_abort(true, false, true);
+ test_loader_abort(true, true, true);
+ test_loader_abort(false, false, false);
+ test_loader_abort(false, true, false);
+ test_loader_abort(true, false, false);
+ test_loader_abort(true, true, false);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_789.cc b/storage/tokudb/PerconaFT/src/tests/test_789.cc
new file mode 100644
index 00000000000..d76c77111c5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_789.cc
@@ -0,0 +1,177 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <memory.h>
+
+static void
+test_789(void) {
+ int r;
+
+ /* setup test directory */
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ /* setup environment */
+ DB_ENV *env;
+ {
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stdout);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+ }
+
+ /* setup database */
+ DB *db;
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+ }
+
+ /* insert, commit */
+ {
+ DB_TXN *txn_master;
+ r = env->txn_begin(env, 0, &txn_master, 0); assert(r == 0);
+ DB_TXN *txn;
+ r = env->txn_begin(env, txn_master, &txn, 0); assert(r == 0);
+ int i;
+ for (i=0; i<3; i++) {
+ int k = htonl(i);
+ int v = 0;
+ DBT key, val;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+ }
+ r = txn->commit(txn, 0); assert(r == 0);
+ r = txn_master->commit(txn_master, 0); assert(r == 0);
+ }
+
+ /* update, rollback */
+ {
+ DB_TXN *txn_master;
+ r = env->txn_begin(env, 0, &txn_master, 0); assert(r == 0);
+ DB_TXN *txn;
+ r = env->txn_begin(env, txn_master, &txn, 0); assert(r == 0);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ DBT key, val;
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_NEXT); assert(r == 0);
+ *(char*)val.data = 1;
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ r = cursor->c_close(cursor); assert(r == 0);
+ toku_free(key.data); toku_free(val.data);
+ r = txn->commit(txn, 0); assert(r == 0);
+ r = txn_master->abort(txn_master); assert(r == 0);
+ }
+
+ /* delete, rollback */
+ {
+ DB_TXN *txn_master;
+ r = env->txn_begin(env, 0, &txn_master, 0); assert(r == 0);
+ DB_TXN *txn;
+ r = env->txn_begin(env, txn_master, &txn, 0); assert(r == 0);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ DBT key, val;
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_NEXT); assert(r == 0);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == 0);
+ r = cursor->c_close(cursor); assert(r == 0);
+ toku_free(key.data); toku_free(val.data);
+ r = txn->commit(txn, 0); assert(r == 0);
+ r = txn_master->abort(txn_master); assert(r == 0);
+ }
+
+ /* update, commit */
+ {
+ DB_TXN *txn_master;
+ r = env->txn_begin(env, 0, &txn_master, 0); assert(r == 0);
+ DB_TXN *txn;
+ r = env->txn_begin(env, txn_master, &txn, 0); assert(r == 0);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ DBT key, val;
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_NEXT); assert(r == 0);
+ *(char*)val.data = 2;
+ r = db->put(db, txn, &key, &val, 0); assert(r == 0);
+ r = cursor->c_close(cursor); assert(r == 0);
+ toku_free(key.data); toku_free(val.data);
+ r = txn->commit(txn, 0); assert(r == 0);
+ r = txn_master->commit(txn_master, 0); assert(r == 0);
+ }
+
+ /* delete, commit */
+ {
+ DB_TXN *txn_master;
+ r = env->txn_begin(env, 0, &txn_master, 0); assert(r == 0);
+ DB_TXN *txn;
+ r = env->txn_begin(env, txn_master, &txn, 0); assert(r == 0);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ DBT key, val;
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_NEXT); assert(r == 0);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == 0);
+ r = cursor->c_close(cursor); assert(r == 0);
+ toku_free(key.data); toku_free(val.data);
+ r = txn->commit(txn, 0); assert(r == 0);
+ r = txn_master->commit(txn_master, 0); assert(r == 0);
+ }
+
+ /* close db */
+ r = db->close(db, 0); assert(r == 0);
+
+ /* close env */
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int UU(argc), char UU(*const argv[])) {
+ test_789();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_935.cc b/storage/tokudb/PerconaFT/src/tests/test_935.cc
new file mode 100644
index 00000000000..ba83b7c0917
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_935.cc
@@ -0,0 +1,132 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <memory.h>
+
+static void
+testit (const int klen, const int vlen, const int n, const int lastvlen) {
+ if (verbose) printf("testit %d %d %d %d\n", klen, vlen, n, lastvlen);
+
+ int r;
+
+ // setup test directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ // setup environment
+ DB_ENV *env;
+ {
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stdout);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+ }
+
+ // setup database
+ DB *db;
+ {
+ DB_TXN *txn = 0;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+ }
+
+ // insert to fill up a node
+ {
+ void *v = toku_malloc(vlen); assert(v); memset(v, 0, vlen);
+ DB_TXN *txn = 0;
+ int i;
+ for (i=0; i<n; i++) {
+ int k = htonl(i);
+ assert(sizeof k == klen);
+ DBT key, val;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, v, vlen), 0);
+ assert(r == 0);
+ }
+ if (lastvlen > 0) {
+ int k = htonl(n);
+ DBT key, val;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, v, lastvlen), 0);
+ assert(r == 0);
+ }
+ toku_free(v);
+ }
+
+ // add another one to force a node split
+ {
+ void *v = toku_malloc(vlen); assert(v); memset(v, 0, vlen);
+ DB_TXN *txn = 0;
+ int k = htonl(n+1);
+ DBT key, val;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, v, vlen), 0);
+ assert(r == 0);
+ toku_free(v);
+ }
+
+ // close db
+ r = db->close(db, 0); assert(r == 0);
+
+ // close env
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ const int meg = 1024*1024;
+ const int headeroverhead = 12*4;
+ const int numentries = 4;
+ const int klen = 4;
+ const int vlen = 4096;
+ const int leafoverhead = 1+8+4+4;
+ const int leafentrysize = leafoverhead+klen+vlen;
+ int n = (meg - headeroverhead - numentries) / leafentrysize;
+ int left = meg - headeroverhead - numentries - n*leafentrysize;
+ int lastvlen = left - leafoverhead - klen;
+ testit(klen, vlen, n, lastvlen-1);
+ testit(klen, vlen, n, lastvlen-0);
+ testit(klen, vlen, n, lastvlen+1);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_abort1.cc b/storage/tokudb/PerconaFT/src/tests/test_abort1.cc
new file mode 100644
index 00000000000..55449610ffb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_abort1.cc
@@ -0,0 +1,194 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Simple test of logging. Can I start PerconaFT with logging enabled? */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+#include <memory.h>
+#include <stdio.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+static void
+test_db_open_aborts (void) {
+ DB_ENV *env;
+ DB *db;
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ {
+ DB_TXN *tid;
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ {
+ DBT key,data;
+ dbt_init(&key, "hello", 6);
+ dbt_init(&data, "there", 6);
+ r=db->put(db, tid, &key, &data, 0);
+ CKERR(r);
+ }
+ r=db->close(db, 0); assert(r==0);
+ r=tid->abort(tid); assert(r==0);
+ }
+ {
+ {
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, "foo.db", sizeof("foo.db"));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname);
+ CKERR2(r, DB_NOTFOUND);
+ }
+ toku_struct_stat statbuf;
+ char filename[TOKU_PATH_MAX+1];
+ r = toku_stat(toku_path_join(filename, 2, TOKU_TEST_FILENAME, "foo.db"), &statbuf);
+ assert(r!=0);
+ assert(errno==ENOENT);
+ }
+
+ r=env->close(env, 0); assert(r==0);
+}
+
+// Do two transactions, one commits, and one aborts. Do them concurrently.
+static void
+test_db_put_aborts (void) {
+ DB_ENV *env;
+ DB *db;
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ {
+ DB_TXN *tid;
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid,0); assert(r==0);
+ }
+ {
+ DB_TXN *tid;
+ DB_TXN *tid2;
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid2, 0); assert(r==0);
+ {
+ DBT key,data;
+ dbt_init(&key, "hello", 6);
+ dbt_init(&data, "there", 6);
+ r=db->put(db, tid, &key, &data, 0);
+ CKERR(r);
+ }
+ {
+ DBT key,data;
+ dbt_init(&key, "bye", 4);
+ dbt_init(&data, "now", 4);
+ r=db->put(db, tid2, &key, &data, 0);
+ CKERR(r);
+ }
+ //printf("%s:%d aborting\n", __FILE__, __LINE__);
+ r=tid->abort(tid); assert(r==0);
+ //printf("%s:%d committing\n", __FILE__, __LINE__);
+ r=tid2->commit(tid2,0); assert(r==0);
+ }
+ // The database should exist
+ {
+ char *filename;
+ {
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, "foo.db", sizeof("foo.db"));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname);
+ CKERR(r);
+ CAST_FROM_VOIDP(filename, iname.data);
+ assert(filename);
+ }
+ toku_struct_stat statbuf;
+ char fullfile[TOKU_PATH_MAX+1];
+ r = toku_stat(toku_path_join(fullfile, 2, TOKU_TEST_FILENAME, filename), &statbuf);
+ assert(r==0);
+ toku_free(filename);
+ }
+ // But the item should not be in it.
+ if (1)
+ {
+ DB_TXN *tid;
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ {
+ DBT key,data;
+ dbt_init(&key, "hello", 6);
+ dbt_init(&data, NULL, 0);
+ r=db->get(db, tid, &key, &data, 0);
+ assert(r!=0);
+ assert(r==DB_NOTFOUND);
+ }
+ {
+ DBT key,data;
+ dbt_init(&key, "bye", 4);
+ dbt_init(&data, NULL, 0);
+ r=db->get(db, tid, &key, &data, 0);
+ CKERR(r);
+ }
+ r=tid->commit(tid,0); assert(r==0);
+ }
+
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+}
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ test_db_open_aborts();
+ test_db_put_aborts();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_abort2.cc b/storage/tokudb/PerconaFT/src/tests/test_abort2.cc
new file mode 100644
index 00000000000..b024248bad4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_abort2.cc
@@ -0,0 +1,147 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Put some insert messages into an internal buffer (by first creating a DB, filling it up, then closing it, and reopening, and inserting a few things)
+ * Then perform a transaction that overwrites some of those internal things.
+ * Then abort the transaction.
+ * Make sure those middle things made it back into the tree.
+ */
+
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *txn;
+
+static void
+insert (int i, int j) {
+ char hello[30], there[230];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "%dthere%d %*s", j, i, 10+i%40, "padding");
+ int r = db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void
+do_test_abort2 (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->set_pagesize(db, 4096); // Use a small page
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ {
+ int i;
+ for (i=0; i<1000; i++) {
+ insert(4*i, 0);
+ }
+ }
+ r=txn->commit(txn, 0); CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+
+ //printf("%s:%d\n", __FILE__, __LINE__);
+
+ // Now do a few inserts that abort.
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ insert(3, 0);
+ insert(5, 0);
+ insert(7, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ insert(7, 1);
+ r=txn->abort(txn); CKERR(r);
+
+
+ //printf("%s:%d\n", __FILE__, __LINE__);
+ //r=db->close(db,0); CKERR(r); r=env->close(env, 0); CKERR(r); return;
+
+ // Don't do a lookup on "hello7", because that will force things out of the buffer.
+ r=db->close(db, 0); CKERR(r);
+ //printf("%s:%d\n", __FILE__, __LINE__);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+ //printf("%s:%d\n", __FILE__, __LINE__);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ {
+ DBT key,data;
+ memset(&data, 0, sizeof(data));
+ r = db->get(db, txn, dbt_init(&key, "hello7", strlen("hello7")+1), &data, 0);
+ CKERR(r);
+ //printf("data is %s\n", (char*)data.data);
+ assert(((char*)data.data)[0]=='0');
+ }
+ r=txn->abort(txn); CKERR(r);
+
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ do_test_abort2();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_abort3.cc b/storage/tokudb/PerconaFT/src/tests/test_abort3.cc
new file mode 100644
index 00000000000..0d4736c728b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_abort3.cc
@@ -0,0 +1,196 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Try to exercise all the cases for the leafcommands in ft-ops.c
+ */
+
+
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *txn;
+
+static void insert (int i, int j) {
+ char hello[30], there[30];
+ DBT key,data;
+ if (verbose) printf("Insert %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", j);
+ int r = db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void op_delete (int i) {
+ char hello[30];
+ DBT key;
+ if (verbose) printf("op_delete %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ int r = db->del(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ DB_DELETE_ANY);
+ assert(r==0);
+}
+
+static void lookup (int i, int expect, int expectj) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ memset(&data, 0, sizeof(data));
+ if (verbose) printf("Looking up %d (expecting %s)\n", i, expect==0 ? "to find" : "not to find");
+ int r = db->get(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &data,
+ 0);
+ assert(expect==r);
+ if (expect==0) {
+ CKERR(r);
+ snprintf(there, sizeof(there), "there%d", expectj);
+ assert(data.size==strlen(there)+1);
+ assert(strcmp((char*)data.data, there)==0);
+ }
+}
+
+static void
+test_abort3 (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ insert(0, 0);
+ r=txn->commit(txn, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ op_delete(0);
+ op_delete(1);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ lookup(1, DB_NOTFOUND, -1);
+ insert(2, 3);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(2, 4);
+ insert(2, 5);
+ lookup(2, 0, 5);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ lookup(2, 0, 5);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(3, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(3, 1);
+ lookup(3, 0, 1);
+ r=txn->abort(txn); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ lookup(3, 0, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(4, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ op_delete(4);
+ lookup(4, DB_NOTFOUND, -1);
+ r=txn->abort(txn); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ lookup(4, 0, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(5, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(5, 1);
+ lookup(5, 0, 1);
+ op_delete(5);
+ lookup(5, DB_NOTFOUND, -1);
+ r=txn->abort(txn); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ lookup(5, 0, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(6, 0);
+ lookup(6, 0, 0);
+ op_delete(6);
+ lookup(6, DB_NOTFOUND, -1);
+ r=txn->abort(txn); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ lookup(6, DB_NOTFOUND, -1);
+ r=txn->commit(txn, 0); CKERR(r);
+
+
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_abort3();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_abort4.cc b/storage/tokudb/PerconaFT/src/tests/test_abort4.cc
new file mode 100644
index 00000000000..e67efb465b2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_abort4.cc
@@ -0,0 +1,263 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+//Verify aborting transactions works properly when transaction
+//starts with an empty db and a table lock.
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+#include <memory.h>
+#include <stdio.h>
+
+
+DB_ENV *env;
+DB *db;
+DB_TXN *null_txn = NULL;
+DB_TXN *txn;
+uint32_t find_num;
+
+long closemode = -1; // must be set to 0 or 1 on command line
+long logsize = -2; // must be set to a number from -1 to 20 inclusive, on command line.
+
+static void
+init(void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+;
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_env_create(&env, 0); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, null_txn, "foo.db", 0, DB_BTREE, DB_CREATE|DB_EXCL, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, null_txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->pre_acquire_table_lock(db, txn); CKERR(r);
+}
+
+static void
+tear_down(void) {
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void
+abort_txn(void) {
+ find_num = 0;
+ int r = txn->abort(txn); CKERR(r);
+ txn = NULL;
+}
+
+static void
+put(uint32_t k, uint32_t v) {
+ int r;
+ DBT key,val;
+
+ dbt_init(&key, &k, sizeof(k));
+ dbt_init(&val, &v, sizeof(v));
+ r = db->put(db, txn, &key, &val, 0); CKERR(r);
+}
+
+static void
+test_insert_and_abort(uint32_t num_to_insert) {
+ find_num = 0;
+
+ uint32_t k;
+ uint32_t v;
+
+ uint32_t i;
+ for (i=0; i < num_to_insert; i++) {
+ k = htonl(i);
+ v = htonl(i+num_to_insert);
+ put(k, v);
+ }
+ abort_txn();
+}
+
+static void
+test_insert_and_abort_and_insert(uint32_t num_to_insert) {
+ test_insert_and_abort(num_to_insert);
+ find_num = num_to_insert / 2;
+ uint32_t k, v;
+ uint32_t i;
+ int r;
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->pre_acquire_table_lock(db, txn); CKERR(r);
+ for (i=0; i < find_num; i++) {
+ k = htonl(i);
+ v = htonl(i+5);
+ put(k, v);
+ }
+ txn->commit(txn, 0);
+ txn = NULL;
+}
+
+#define bit0 (1<<0)
+#define bit1 (1<<1)
+
+static int
+do_nothing(DBT const *UU(a), DBT const *UU(b), void *UU(c)) {
+ return 0;
+}
+
+static void
+verify_and_tear_down(int close_first) {
+ int r;
+ {
+ char *filename;
+ {
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, "foo.db", sizeof("foo.db"));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname);
+ CKERR(r);
+ CAST_FROM_VOIDP(filename, iname.data);
+ assert(filename);
+ }
+ toku_struct_stat statbuf;
+ char fullfile[TOKU_PATH_MAX+1];
+ r = toku_stat(toku_path_join(fullfile, 2, TOKU_TEST_FILENAME, filename), &statbuf);
+ assert(r==0);
+ toku_free(filename);
+ }
+ CKERR(r);
+ if (close_first) {
+ r=db->close(db, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, null_txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ }
+ DBC *cursor;
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ uint32_t found = 0;
+ do {
+ r = cursor->c_getf_next(cursor, 0, do_nothing, NULL);
+ if (r==0) found++;
+ } while (r==0);
+ CKERR2(r, DB_NOTFOUND);
+ cursor->c_close(cursor);
+ txn->commit(txn, 0);
+ assert(found==find_num);
+ tear_down();
+}
+
+static void
+runtests(void) {
+ int close_first = closemode;
+ if (logsize == -1) {
+ init();
+ abort_txn();
+ verify_and_tear_down(close_first);
+ } else {
+ uint32_t n = 1<<logsize;
+ {
+ if (verbose) {
+ printf("\t%s:%d-%s() close_first=%d n=%06x\n",
+ __FILE__, __LINE__, __FUNCTION__, close_first, n);
+ fflush(stdout);
+ }
+ init();
+ test_insert_and_abort(n);
+ verify_and_tear_down(close_first);
+
+ init();
+ test_insert_and_abort_and_insert(n);
+ verify_and_tear_down(close_first);
+ }
+ }
+}
+
+static long parseint (const char *str) {
+ errno = 0;
+ char *end;
+ long v = strtol(str, &end, 10);
+ assert(errno==0 && *end==0);
+ return v;
+}
+
+static void
+parse_my_args (int argc, char * const argv[]) {
+ const char *argv0=argv[0];
+ while (argc>1) {
+ int resultcode=0;
+ if (strcmp(argv[1], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[1],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[1],"-c") == 0 && argc > 2) {
+ argc--; argv++;
+ closemode = parseint(argv[1]);
+ } else if (strcmp(argv[1],"-l") == 0 && argc > 2) {
+ argc--; argv++;
+ logsize = parseint(argv[1]);
+ } else if (strcmp(argv[1], "-h")==0) {
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q] [-h] -c <closemode (0 or 1)> -l <log of size, -1, or 0 through 20>\n", argv0);
+ exit(resultcode);
+ } else {
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+ assert(closemode==0 || closemode==1);
+ assert(logsize >= -1 && logsize <=20);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_my_args(argc, argv);
+
+ runtests();
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_abort5.cc b/storage/tokudb/PerconaFT/src/tests/test_abort5.cc
new file mode 100644
index 00000000000..728afcce1b9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_abort5.cc
@@ -0,0 +1,251 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+//Verify aborting transactions works properly when transaction
+//starts with an empty db and a table lock.
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+#include <memory.h>
+#include <stdio.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+DB *db;
+DB_TXN *null_txn = NULL;
+DB_TXN *txn;
+DB_TXN *childtxn;
+uint32_t find_num;
+
+static void
+init(void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_env_create(&env, 0); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, null_txn, "foo.db", 0, DB_BTREE, DB_CREATE|DB_EXCL, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, null_txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->pre_acquire_table_lock(db, txn); CKERR(r);
+ r=env->txn_begin(env, txn, &childtxn, 0); CKERR(r);
+}
+
+static void
+tear_down(void) {
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void
+abort_childtxn(void) {
+ find_num = 0;
+ int r;
+ r = txn->abort(childtxn); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ childtxn = NULL;
+ txn = NULL;
+}
+
+static void
+abort_both(void) {
+ find_num = 0;
+ int r;
+ r = txn->abort(childtxn); CKERR(r);
+ r = txn->abort(txn); CKERR(r);
+ childtxn = NULL;
+ txn = NULL;
+}
+
+static void
+abort_parent(void) {
+ int r = txn->abort(txn); CKERR(r);
+}
+
+static void
+abort_txn(int type) {
+ if (type==0) abort_parent();
+ else if (type==1) abort_childtxn();
+ else if (type==2) abort_both();
+ else assert(false);
+
+ find_num = 0;
+ childtxn = NULL;
+ txn = NULL;
+}
+
+static void
+put(uint32_t k, uint32_t v) {
+ int r;
+ DBT key,val;
+ static uint32_t kvec[128];
+ static uint32_t vvec[128];
+
+ kvec[0] = k;
+ vvec[0] = v;
+ dbt_init(&key, &kvec[0], sizeof(kvec));
+ dbt_init(&val, &vvec[0], sizeof(vvec));
+ r = db->put(db, childtxn ? childtxn : txn, &key, &val, 0); CKERR(r);
+}
+
+static void
+test_insert_and_abort(uint32_t num_to_insert, int abort_type) {
+ if (verbose>1) printf("\t" __FILE__ ": insert+abort(%u,%d)\n", num_to_insert, abort_type);
+ find_num = 0;
+
+ uint32_t k;
+ uint32_t v;
+
+ uint32_t i;
+ for (i=0; i < num_to_insert; i++) {
+ k = htonl(i);
+ v = htonl(i+num_to_insert);
+ put(k, v);
+ }
+ abort_txn(abort_type);
+}
+
+static void
+test_insert_and_abort_and_insert(uint32_t num_to_insert, int abort_type) {
+ if (verbose>1) printf("\t" __FILE__ ": insert+abort+insert(%u,%d)\n", num_to_insert, abort_type);
+ test_insert_and_abort(num_to_insert, abort_type);
+ find_num = num_to_insert / 2;
+ uint32_t k, v;
+ uint32_t i;
+ for (i=0; i < find_num; i++) {
+ k = htonl(i);
+ v = htonl(i+5);
+ put(k, v);
+ }
+}
+
+#define bit0 (1<<0)
+#define bit1 (1<<1)
+
+static int
+do_nothing(DBT const *UU(a), DBT const *UU(b), void *UU(c)) {
+ return 0;
+}
+
+static void
+verify_and_tear_down(int close_first) {
+ int r;
+ {
+ char *filename;
+ {
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, "foo.db", sizeof("foo.db"));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname);
+ CKERR(r);
+ CAST_FROM_VOIDP(filename, iname.data);
+ assert(filename);
+ }
+ toku_struct_stat statbuf;
+ char fullfile[TOKU_PATH_MAX+1];
+ r = toku_stat(toku_path_join(fullfile, 2, TOKU_TEST_FILENAME, filename), &statbuf);
+ assert(r==0);
+ toku_free(filename);
+ }
+ if (close_first) {
+ r=db->close(db, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, null_txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR(r);
+ }
+ DBC *cursor;
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ uint32_t found = 0;
+ do {
+ r = cursor->c_getf_next(cursor, 0, do_nothing, NULL);
+ if (r==0) found++;
+ } while (r==0);
+ CKERR2(r, DB_NOTFOUND);
+ cursor->c_close(cursor);
+ txn->commit(txn, 0);
+ assert(found==find_num);
+ tear_down();
+}
+
+static void
+runtests(int abort_type) {
+ if (verbose) printf("\t" __FILE__ ": runtests(%d)\n", abort_type);
+ int close_first;
+ for (close_first = 0; close_first < 2; close_first++) {
+ init();
+ abort_txn(abort_type);
+ verify_and_tear_down(close_first);
+ uint32_t n;
+ for (n = 1; n < 1<<10; n*=2) {
+ init();
+ test_insert_and_abort(n, abort_type);
+ verify_and_tear_down(close_first);
+
+ init();
+ test_insert_and_abort_and_insert(n, abort_type);
+ verify_and_tear_down(close_first);
+ }
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ int abort_type;
+ for (abort_type = 0; abort_type<3; abort_type++) {
+ runtests(abort_type);
+ }
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_abort_delete_first.cc b/storage/tokudb/PerconaFT/src/tests/test_abort_delete_first.cc
new file mode 100644
index 00000000000..3dd7258e22e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_abort_delete_first.cc
@@ -0,0 +1,174 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Test what happens if we have deleted the first thing in the database.
+ * Also the last.
+ * Also if we've deleted a lot of stuff, so that the first good thing is not on the first page.
+ */
+
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *txn;
+
+static void
+insert (int i) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ int r = db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void
+op_delete (int i) {
+ char hello[30];
+ DBT key;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ int r = db->del(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ 0);
+ CKERR(r);
+}
+
+static void
+find (int i) {
+ char hello[30];
+ DBT key, val;
+ memset(&val,0,sizeof(val));
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ int r = db->get(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &val,
+ 0);
+ CKERR(r);
+}
+
+static void
+find_first_or_last (int i, int cflag) {
+ int r;
+ DBC *cursor;
+ DBT key, val;
+ memset(&key,0,sizeof(key));
+ memset(&val,0,sizeof(val));
+
+ r = db->cursor(db, txn, &cursor, 0);
+ CKERR(r);
+ r = cursor->c_get(cursor, &key, &val, cflag);
+ assert(r==0);
+
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+
+ assert(strcmp(hello, (char*)key.data)==0);
+ assert(strcmp(there, (char*)val.data)==0);
+
+ r = cursor->c_close(cursor);
+}
+
+static void
+do_abort_delete_first_or_last(int N,
+ int first // 1 for first, 0 for last
+ ) {
+ int r,i;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->set_pagesize(db, 4096); // Use a small page
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); assert(r==0);
+
+ // First fill up the db
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+
+ for (i=0; i<N; i++) {
+ insert(i);
+ }
+ r=txn->commit(txn, 0); CKERR(r);
+
+ // Now op_delete a bunch of stuff and see if we can do DB_FIRST
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ if (first) {
+ for (i=0; i<N-1; i++) {
+ op_delete(i);
+ }
+ find(i);
+ find_first_or_last(i, DB_FIRST);
+ } else {
+ for (i=1; i<N; i++) {
+ op_delete(i);
+ }
+ find_first_or_last(0, DB_LAST);
+ }
+
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+
+ // Oh man, this is gross.
+ char cmd[sizeof("../../tools/tokudb_dump -h foo.db >") + 2 * TOKU_PATH_MAX];
+ snprintf(cmd, sizeof(cmd), "../../tools/tokudb_dump -h %s foo.db > %s", TOKU_TEST_FILENAME, DEV_NULL_FILE);
+ r=system(cmd);
+ CKERR(r);
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ int f;
+ for (f=0; f<2; f++) {
+ do_abort_delete_first_or_last(10, f);
+ do_abort_delete_first_or_last(1000,f);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_archive0.cc b/storage/tokudb/PerconaFT/src/tests/test_archive0.cc
new file mode 100644
index 00000000000..0e3b34750d8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_archive0.cc
@@ -0,0 +1,73 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test log archive. */
+#include <db.h>
+#include <sys/stat.h>
+
+
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *txn;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ char **list;
+ r=env->log_archive(env, &list, 0);
+
+ assert(list==0);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_archive1.cc b/storage/tokudb/PerconaFT/src/tests/test_archive1.cc
new file mode 100644
index 00000000000..9a797a209cf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_archive1.cc
@@ -0,0 +1,93 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test log archive. */
+#include <db.h>
+#include <sys/stat.h>
+#include <memory.h>
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *txn;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->set_lg_max(env, 16000); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ int i;
+ for (i=0; i<400; i++) {
+ DBT key,data;
+ char hello[30],there[30];
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ r=txn->commit(txn, 0); CKERR(r);
+ r=env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ }
+
+ {
+ char **list;
+ r=env->log_archive(env, &list, 0);
+ CKERR(r);
+ //this test no longer produces a list with any entries for TDB
+ // - txn_checkpoint trims unused logfiles
+ assert(list == 0);
+ }
+
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_archive2.cc b/storage/tokudb/PerconaFT/src/tests/test_archive2.cc
new file mode 100644
index 00000000000..a3c88b10ae1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_archive2.cc
@@ -0,0 +1,102 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test log archive. */
+#include <db.h>
+#include <sys/stat.h>
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *env;
+ DB *db, *db2;
+ DB_TXN *txn, *txn2;
+ DBT key,data;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->set_lg_max(env, 20000); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=db_create(&db2, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->open(db2, txn, "foo2.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn2, 0); CKERR(r);
+ r=db->put(db2, txn2, dbt_init(&key, "what", 5), dbt_init(&data, "who", 4), 0); CKERR(r);
+
+ int i;
+ for (i=0; i<100; i++) {
+ char hello[30],there[30];
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db->put(db, txn,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ r=txn->commit(txn, 0); CKERR(r);
+ r=env->txn_checkpoint(env, 0, 0, 0); CKERR(r);
+ }
+
+ {
+ char **list;
+ r=env->log_archive(env, &list, 0);
+ CKERR(r);
+ assert(list==0); // since there is an open txn
+ }
+
+ r=txn2->commit(txn2, 0); CKERR(r);
+
+ r=db->close(db, 0); CKERR(r);
+ r=db2->close(db2, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_bad_implicit_promotion.cc b/storage/tokudb/PerconaFT/src/tests/test_bad_implicit_promotion.cc
new file mode 100644
index 00000000000..cdc2ee4d947
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_bad_implicit_promotion.cc
@@ -0,0 +1,138 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// Test the following scenario:
+// Begin A
+// A deletes key K
+// A aborts
+// Begin B
+// B deletes key K-1
+// B deletes key K
+// B deletes key K+1
+// B commits
+// Begin C
+// C queries K, should read K (not the delete!).
+//
+// An incorrect mvcc implementation would 'implicitly' promote
+// A's delete to committed, based on the fact that the oldest
+// referenced xid at the time of injection for key k-1 and k+1
+// is greater than A's xid.
+
+static void test_insert_bad_implicit_promotion(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_cachesize(env, 1, 0, 1); CKERR(r); // 1gb cache so this test fits in memory
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL+DB_INIT_TXN, 0); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 4096); CKERR(r);
+ r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ const int val_size = 512;
+
+ DBT key;
+ DBT val;
+ char *XMALLOC_N(val_size, val_buf);
+ memset(val_buf, 'x', val_size);
+ dbt_init(&val, val_buf, val_size);
+
+ // Insert rows [0, N]
+ const int N = 1000;
+ for (int i = 0; i < N; i++) {
+ int k = toku_htonl(i);
+ dbt_init(&key, &k, sizeof(k));
+ r = db->put(db, NULL, &key, &val, 0); CKERR(r);
+ }
+
+ int key_500 = toku_htonl(500);
+ int key_499 = toku_htonl(499);
+ int key_501 = toku_htonl(501);
+ // sanity check our keys
+ r = db->get(db, NULL, dbt_init(&key, &key_500, sizeof(key_500)), &val, 0); CKERR(r);
+ r = db->get(db, NULL, dbt_init(&key, &key_500, sizeof(key_499)), &val, 0); CKERR(r);
+ r = db->get(db, NULL, dbt_init(&key, &key_500, sizeof(key_501)), &val, 0); CKERR(r);
+
+ // Abort a delete for key 500
+ DB_TXN *txn_A;
+ r = env->txn_begin(env, NULL, &txn_A, DB_SERIALIZABLE); CKERR(r);
+ dbt_init(&key, &key_500, sizeof(key_500));
+ r = db->del(db, txn_A, &key, DB_DELETE_ANY); CKERR(r);
+ r = txn_A->abort(txn_A); CKERR(r);
+
+ // Commit two deletes on keys 499 and 501. This should inject
+ // at least one message in the same buffer that has the delete/abort
+ // messages for key 500.
+ DB_TXN *txn_B;
+ r = env->txn_begin(env, NULL, &txn_B, DB_SERIALIZABLE); CKERR(r);
+ dbt_init(&key, &key_499, sizeof(key_499));
+ r = db->del(db, txn_B, &key, DB_DELETE_ANY); CKERR(r);
+ dbt_init(&key, &key_501, sizeof(key_501));
+ r = db->del(db, txn_B, &key, DB_DELETE_ANY); CKERR(r);
+ r = txn_B->commit(txn_B, 0); CKERR(r);
+
+ // No transactions are live - so when we create txn C, the oldest
+ // referenced xid will be txn C. If our implicit promotion logic is
+ // wrong, we will use txn C's xid to promote the delete on key 500
+ // before the abort message hits it, and C's query will return nothing.
+ DB_TXN *txn_C;
+ dbt_init(&key, &key_500, sizeof(key_500));
+ r = env->txn_begin(env, NULL, &txn_C, DB_TXN_SNAPSHOT); CKERR(r);
+ r = db->get(db, txn_C, &key, &val, 0); CKERR(r);
+ r = txn_C->commit(txn_C, 0); CKERR(r);
+
+ toku_free(val_buf);
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ test_insert_bad_implicit_promotion();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_blobs_leaf_split.cc b/storage/tokudb/PerconaFT/src/tests/test_blobs_leaf_split.cc
new file mode 100644
index 00000000000..0cb7c6196b8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_blobs_leaf_split.cc
@@ -0,0 +1,141 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// verify that the leaf split code can handle rows larger than nodesize
+
+#include "test.h"
+
+static void insert(DB *db, DB_TXN *txn, int k, int val_size) {
+ int r;
+
+ // generate the key
+ char key_buffer[8];
+ memset(key_buffer, 0, sizeof key_buffer);
+ int newa = htonl(k);
+ memcpy(key_buffer, &newa, sizeof newa);
+
+ // generate the value
+ char *XCALLOC_N(val_size, val_buffer);
+
+ DBT key = { .data = key_buffer, .size = sizeof key_buffer };
+ DBT value = { .data = val_buffer, .size = (uint32_t) val_size };
+ r = db->put(db, txn, &key, &value, 0); assert_zero(r);
+
+ toku_free(val_buffer);
+}
+
+int test_main(int argc, char * const argv[]) {
+ const char *db_env_dir = "dir.blobs.leafsplit.env.tdb";
+ int db_env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
+ const char *db_filename = "blobs.db";
+ int do_txn = 1;
+ uint64_t cachesize = 0;
+ uint32_t pagesize = 0;
+
+ int i;
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ if (strcmp(arg, "-v") == 0 || strcmp(arg, "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ if (verbose > 0) verbose--;
+ continue;
+ }
+ if (strcmp(arg, "--txn") == 0 && i+1 < argc) {
+ do_txn = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--pagesize") == 0 && i+1 < argc) {
+ pagesize = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--cachesize") == 0 && i+1 < argc) {
+ cachesize = atol(argv[++i]);
+ continue;
+ }
+
+ assert(0);
+ }
+
+ int r;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+ r = system(rm_cmd); assert_zero(r);
+
+ r = toku_os_mkdir(db_env_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); assert_zero(r);
+
+ // create and open the env
+ DB_ENV *db_env = NULL;
+ r = db_env_create(&db_env, 0); assert_zero(r);
+ if (cachesize) {
+ const uint64_t gig = 1 << 30;
+ r = db_env->set_cachesize(db_env, cachesize / gig, cachesize % gig, 1); assert_zero(r);
+ }
+ if (!do_txn)
+ db_env_open_flags &= ~(DB_INIT_TXN | DB_INIT_LOG);
+ r = db_env->open(db_env, db_env_dir, db_env_open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ // create the db
+ DB *db = NULL;
+ r = db_create(&db, db_env, 0); assert_zero(r);
+ DB_TXN *create_txn = NULL;
+ if (do_txn) {
+ r = db_env->txn_begin(db_env, NULL, &create_txn, 0); assert_zero(r);
+ }
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert_zero(r);
+ }
+ r = db->open(db, create_txn, db_filename, NULL, DB_BTREE, DB_CREATE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); assert_zero(r);
+
+ insert(db, create_txn, 1, 8000000);
+ insert(db, create_txn, 1, 16000000);
+ insert(db, create_txn, 1, 32000000);
+ insert(db, create_txn, 2, 1);
+
+ if (do_txn) {
+ r = create_txn->commit(create_txn, 0); assert_zero(r);
+ }
+
+ // shutdown
+ r = db->close(db, 0); assert_zero(r); db = NULL;
+ r = db_env->close(db_env, 0); assert_zero(r); db_env = NULL;
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_bulk_fetch.cc b/storage/tokudb/PerconaFT/src/tests/test_bulk_fetch.cc
new file mode 100644
index 00000000000..bdebc9aebc2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_bulk_fetch.cc
@@ -0,0 +1,305 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+verify_val(DBT const *a, DBT const *b, void *c) {
+ assert(a->size == sizeof(uint64_t));
+ assert(b->size == sizeof(uint64_t));
+ uint64_t* expected = (uint64_t *)c;
+ assert(*expected == *(uint64_t *)a->data);
+ assert(*expected == *(uint64_t *)b->data);
+}
+
+static int
+verify_fwd_fast(DBT const *a, DBT const *b, void *c) {
+ verify_val(a,b,c);
+ uint64_t* expected = (uint64_t *)c;
+ *expected = *expected + 1;
+ return TOKUDB_CURSOR_CONTINUE;
+}
+
+static int
+verify_fwd_slow(DBT const *a, DBT const *b, void *c) {
+ verify_val(a,b,c);
+ uint64_t* expected = (uint64_t *)c;
+ *expected = *expected + 1;
+ return 0;
+}
+
+static int
+verify_bwd_fast(DBT const *a, DBT const *b, void *c) {
+ verify_val(a,b,c);
+ uint64_t* expected = (uint64_t *)c;
+ *expected = *expected - 1;
+ return TOKUDB_CURSOR_CONTINUE;
+}
+
+static int
+verify_bwd_slow(DBT const *a, DBT const *b, void *c) {
+ verify_val(a,b,c);
+ uint64_t* expected = (uint64_t *)c;
+ *expected = *expected - 1;
+ return 0;
+}
+
+uint64_t num_pivots_fetched_prefetch;
+uint64_t num_basements_decompressed_aggressive;
+uint64_t num_basements_decompressed_prefetch;
+uint64_t num_basements_fetched_aggressive;
+uint64_t num_basements_fetched_prefetch;
+
+static void
+init_eng_stat_vars(DB_ENV* env) {
+ num_pivots_fetched_prefetch = get_engine_status_val(env, "FT_NUM_PIVOTS_FETCHED_PREFETCH");
+ num_basements_decompressed_aggressive = get_engine_status_val(env, "FT_NUM_BASEMENTS_DECOMPRESSED_AGGRESSIVE");
+ num_basements_decompressed_prefetch = get_engine_status_val(env, "FT_NUM_BASEMENTS_DECOMPRESSED_PREFETCH");
+ num_basements_fetched_aggressive = get_engine_status_val(env, "FT_NUM_BASEMENTS_FETCHED_AGGRESSIVE");
+ num_basements_fetched_prefetch = get_engine_status_val(env, "FT_NUM_BASEMENTS_FETCHED_PREFETCH");
+}
+
+static void
+check_eng_stat_vars_unchanged(DB_ENV* env) {
+ assert(num_pivots_fetched_prefetch == get_engine_status_val(env, "FT_NUM_PIVOTS_FETCHED_PREFETCH"));
+ assert(num_basements_decompressed_aggressive == get_engine_status_val(env, "FT_NUM_BASEMENTS_DECOMPRESSED_AGGRESSIVE"));
+ assert(num_basements_decompressed_prefetch == get_engine_status_val(env, "FT_NUM_BASEMENTS_DECOMPRESSED_PREFETCH"));
+ assert(num_basements_fetched_aggressive == get_engine_status_val(env, "FT_NUM_BASEMENTS_FETCHED_AGGRESSIVE"));
+ assert(num_basements_fetched_prefetch == get_engine_status_val(env, "FT_NUM_BASEMENTS_FETCHED_PREFETCH"));
+}
+
+static void
+print_relevant_eng_stat_vars(DB_ENV* env) {
+ printf("num_pivots_fetched_prefetch %" PRId64 " \n", get_engine_status_val(env, "FT_NUM_PIVOTS_FETCHED_PREFETCH"));
+ printf("num_basements_decompressed_aggressive %" PRId64 " \n", get_engine_status_val(env, "FT_NUM_BASEMENTS_DECOMPRESSED_AGGRESSIVE"));
+ printf("num_basements_decompressed_prefetch %" PRId64 " \n", get_engine_status_val(env, "FT_NUM_BASEMENTS_DECOMPRESSED_PREFETCH"));
+ printf("num_basements_fetched_aggressive %" PRId64 " \n", get_engine_status_val(env, "FT_NUM_BASEMENTS_FETCHED_AGGRESSIVE"));
+ printf("num_basements_fetched_prefetch %" PRId64 " \n", get_engine_status_val(env, "FT_NUM_BASEMENTS_FETCHED_PREFETCH"));
+}
+
+static void
+test_bulk_fetch (uint64_t n, bool prelock, bool disable_prefetching) {
+ if (verbose) printf("test_rand_insert:%" PRId64 " \n", n);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.bulk_fetch.ft_handle";
+ int r;
+
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r=env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ // arbitrarily have cachetable size be 4*n
+ // goal is to make it small enough such that all of data
+ // does not fit in cachetable, but not so small that we get thrashing
+ r = env->set_cachesize(env, 0, (uint32_t)4*n, 1); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_flags(db, 0);
+ assert(r == 0);
+ r = db->set_pagesize(db, 4096);
+ assert(r == 0);
+ r = db->set_readpagesize(db, 1024);
+ assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ uint64_t keys[n];
+ uint64_t i;
+ for (i=0; i<n; i++) {
+ keys[i] = i;
+ }
+
+ for (i=0; i<n; i++) {
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &keys[i], sizeof keys[i]), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ //
+ // data inserted, now verify that using TOKUDB_CURSOR_CONTINUE in the callback works
+ //
+ DBC* cursor;
+
+ // verify fast
+ uint32_t flags = disable_prefetching ? DBC_DISABLE_PREFETCHING : 0;
+ if (disable_prefetching) {
+ init_eng_stat_vars(env);
+ }
+ r = db->cursor(db, NULL, &cursor, flags);
+ CKERR(r);
+ if (prelock) {
+ r = cursor->c_set_bounds(
+ cursor,
+ db->dbt_neg_infty(),
+ db->dbt_pos_infty(),
+ true,
+ 0
+ );
+ CKERR(r);
+ }
+ uint64_t expected = 0;
+ while (r != DB_NOTFOUND) {
+ r = cursor->c_getf_next(cursor, 0, verify_fwd_fast, &expected);
+ assert(r==0 || r==DB_NOTFOUND);
+ }
+ r = cursor->c_close(cursor); CKERR(r);
+ if (disable_prefetching) {
+ check_eng_stat_vars_unchanged(env);
+ }
+ if (verbose) {
+ print_relevant_eng_stat_vars(env);
+ }
+
+ // verify slow
+ if (disable_prefetching) {
+ init_eng_stat_vars(env);
+ }
+ r = db->cursor(db, NULL, &cursor, flags);
+ CKERR(r);
+ if (prelock) {
+ r = cursor->c_set_bounds(
+ cursor,
+ db->dbt_neg_infty(),
+ db->dbt_pos_infty(),
+ true,
+ 0
+ );
+ CKERR(r);
+ }
+ expected = 0;
+ while (r != DB_NOTFOUND) {
+ r = cursor->c_getf_next(cursor, 0, verify_fwd_slow, &expected);
+ assert(r==0 || r==DB_NOTFOUND);
+ }
+ r = cursor->c_close(cursor); CKERR(r);
+ if (disable_prefetching) {
+ check_eng_stat_vars_unchanged(env);
+ }
+ if (verbose) {
+ print_relevant_eng_stat_vars(env);
+ }
+
+ // now do backwards
+ if (disable_prefetching) {
+ init_eng_stat_vars(env);
+ }
+ r = db->cursor(db, NULL, &cursor, flags);
+ CKERR(r);
+ if (prelock) {
+ r = cursor->c_set_bounds(
+ cursor,
+ db->dbt_neg_infty(),
+ db->dbt_pos_infty(),
+ true,
+ 0
+ );
+ CKERR(r);
+ }
+ expected = n-1;
+ while (r != DB_NOTFOUND) {
+ r = cursor->c_getf_prev(cursor, 0, verify_bwd_fast, &expected);
+ assert(r==0 || r==DB_NOTFOUND);
+ }
+ r = cursor->c_close(cursor); CKERR(r);
+ if (disable_prefetching) {
+ check_eng_stat_vars_unchanged(env);
+ }
+ if (verbose) {
+ print_relevant_eng_stat_vars(env);
+ }
+
+ // verify slow
+ if (disable_prefetching) {
+ init_eng_stat_vars(env);
+ }
+ r = db->cursor(db, NULL, &cursor, flags);
+ CKERR(r);
+ if (prelock) {
+ r = cursor->c_set_bounds(
+ cursor,
+ db->dbt_neg_infty(),
+ db->dbt_pos_infty(),
+ true,
+ 0
+ );
+ CKERR(r);
+ }
+ expected = n-1;
+ while (r != DB_NOTFOUND) {
+ r = cursor->c_getf_prev(cursor, 0, verify_bwd_slow, &expected);
+ assert(r==0 || r==DB_NOTFOUND);
+ }
+ r = cursor->c_close(cursor); CKERR(r);
+ if (disable_prefetching) {
+ check_eng_stat_vars_unchanged(env);
+ }
+ if (verbose) {
+ print_relevant_eng_stat_vars(env);
+ }
+
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_bulk_fetch(10000, false, true);
+ test_bulk_fetch(10000, true, true);
+ test_bulk_fetch(10000, false, false);
+ test_bulk_fetch(10000, true, false);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cachesize.cc b/storage/tokudb/PerconaFT/src/tests/test_cachesize.cc
new file mode 100644
index 00000000000..577f4689fbd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cachesize.cc
@@ -0,0 +1,114 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static uint64_t
+size_from (uint32_t gbytes, uint32_t bytes) {
+ return ((uint64_t)gbytes << 30) + bytes;
+}
+
+static inline void
+size_to (uint64_t s, uint32_t *gbytes, uint32_t *bytes) {
+ *gbytes = s >> 30;
+ *bytes = s & ((1<<30) - 1);
+}
+
+static inline void
+expect_le (uint64_t a, uint32_t gbytes, uint32_t bytes) {
+ uint64_t b = size_from(gbytes, bytes);
+ if (a != b && verbose)
+ printf("WARNING: expect %" PRIu64 " got %" PRIu64 "\n", a, b);
+ assert(a <= b);
+}
+
+
+static void
+test_cachesize (void) {
+#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
+ int r;
+ DB_ENV *env;
+ uint32_t gbytes, bytes; int ncache;
+
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->get_cachesize(env, &gbytes, &bytes, &ncache); assert(r == 0);
+ if (verbose) printf("default %u %u %d\n", gbytes, bytes, ncache);
+
+ r = env->set_cachesize(env, 0, 0, 1); assert(r == 0);
+ r = env->get_cachesize(env, &gbytes, &bytes, &ncache); assert(r == 0);
+ if (verbose) printf("minimum %u %u %d\n", gbytes, bytes, ncache);
+ uint64_t minsize = size_from(gbytes, bytes);
+
+ uint64_t s = 1; size_to(s, &gbytes, &bytes);
+ while (gbytes <= 32) {
+ r = env->set_cachesize(env, gbytes, bytes, ncache);
+ if (r != 0) {
+ if (verbose) printf("max %u %u\n", gbytes, bytes);
+ break;
+ }
+ assert(r == 0);
+ r = env->get_cachesize(env, &gbytes, &bytes, &ncache); assert(r == 0);
+ assert(ncache == 1);
+ if (s <= minsize)
+ expect_le(minsize, gbytes, bytes);
+ else
+ expect_le(s, gbytes, bytes);
+ s *= 2; size_to(s, &gbytes, &bytes);
+ }
+ r = env->close(env, 0); assert(r == 0);
+#endif
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_cachesize();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cmp_descriptor.cc b/storage/tokudb/PerconaFT/src/tests/test_cmp_descriptor.cc
new file mode 100644
index 00000000000..9aff47ed42f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cmp_descriptor.cc
@@ -0,0 +1,281 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+bool cmp_desc_is_four;
+uint32_t four_byte_desc = 0xffffffff;
+uint64_t eight_byte_desc = 0x12345678ffffffff;
+
+
+static int generate_row_for_put(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *dest_key_arrays,
+ DBT_ARRAY *dest_val_arrays,
+ const DBT *src_key,
+ const DBT *src_val
+ )
+{
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ dest_key->data = src_key->data;
+ dest_key->size = src_key->size;
+ dest_key->flags = 0;
+ dest_val->data = src_val->data;
+ dest_val->size = src_val->size;
+ dest_val->flags = 0;
+ return 0;
+}
+static void assert_cmp_desc_valid (DB* db) {
+ if (cmp_desc_is_four) {
+ assert(db->cmp_descriptor->dbt.size == sizeof(four_byte_desc));
+ }
+ else {
+ assert(db->cmp_descriptor->dbt.size == sizeof(eight_byte_desc));
+ }
+ unsigned char* CAST_FROM_VOIDP(cmp_desc_data, db->cmp_descriptor->dbt.data);
+ assert(cmp_desc_data[0] == 0xff);
+ assert(cmp_desc_data[1] == 0xff);
+ assert(cmp_desc_data[2] == 0xff);
+ assert(cmp_desc_data[3] == 0xff);
+}
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint64_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static int
+desc_int64_dbt_cmp (DB *db, const DBT *a, const DBT *b) {
+ assert_cmp_desc_valid(db);
+ assert(a);
+ assert(b);
+
+ assert(a->size == sizeof(int64_t));
+ assert(b->size == sizeof(int64_t));
+
+ int64_t x = *(int64_t *) a->data;
+ int64_t y = *(int64_t *) b->data;
+
+ if (x<y) return -1;
+ if (x>y) return 1;
+ return 0;
+}
+
+
+static void open_env(void) {
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ int r = env->set_default_bt_compare(env, desc_int64_dbt_cmp); CKERR(r);
+ //r = env->set_cachesize(env, 0, 500000, 1); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, generate_row_for_put); CKERR(r);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ open_env();
+}
+
+static void cleanup (void) {
+ int chk_r = env->close(env, 0);
+ CKERR(chk_r);
+ env = NULL;
+}
+
+static void do_inserts_and_queries(DB* db) {
+ int r = 0;
+ DB_TXN* write_txn = NULL;
+ r = env->txn_begin(env, NULL, &write_txn, 0);
+ CKERR(r);
+ for (int i = 0; i < 2000; i++) {
+ uint64_t key_data = random();
+ uint64_t val_data = random();
+ DBT key, val;
+ dbt_init(&key, &key_data, sizeof(key_data));
+ dbt_init(&val, &val_data, sizeof(val_data));
+ { int chk_r = db->put(db, write_txn, &key, &val, 0); CKERR(chk_r); }
+ }
+ r = write_txn->commit(write_txn, 0);
+ CKERR(r);
+ for (int i = 0; i < 2; i++) {
+ DB_TXN* read_txn = NULL;
+ r = env->txn_begin(env, NULL, &read_txn, 0);
+ CKERR(r);
+ DBC* cursor = NULL;
+ r = db->cursor(db, read_txn, &cursor, 0);
+ CKERR(r);
+ if (i == 0) {
+ r = cursor->c_set_bounds(
+ cursor,
+ db->dbt_neg_infty(),
+ db->dbt_pos_infty(),
+ true,
+ 0
+ );
+ CKERR(r);
+ }
+ while(r != DB_NOTFOUND) {
+ DBT key, val;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ assert(r == 0 || r == DB_NOTFOUND);
+ }
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = read_txn->commit(read_txn, 0);
+ CKERR(r);
+ }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+ int r;
+ cmp_desc_is_four = true;
+
+ DBT orig_desc;
+ memset(&orig_desc, 0, sizeof(orig_desc));
+ orig_desc.size = sizeof(four_byte_desc);
+ orig_desc.data = &four_byte_desc;
+
+ DBT other_desc;
+ memset(&other_desc, 0, sizeof(other_desc));
+ other_desc.size = sizeof(eight_byte_desc);
+ other_desc.data = &eight_byte_desc;
+
+ DB_LOADER *loader = NULL;
+ DBT key, val;
+ uint64_t k = 0;
+ uint64_t v = 0;
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ assert(db->descriptor == NULL);
+ r = db->set_pagesize(db, 2048);
+ CKERR(r);
+ r = db->set_readpagesize(db, 1024);
+ CKERR(r);
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ assert(db->descriptor->dbt.size == 0);
+ assert(db->cmp_descriptor->dbt.size == 0);
+ { int chk_r = db->change_descriptor(db, txn_create, &orig_desc, DB_UPDATE_CMP_DESCRIPTOR); CKERR(chk_r); }
+ assert_desc_four(db);
+ assert_cmp_desc_valid(db);
+ r = env->create_loader(env, txn_create, &loader, db, 1, &db, NULL, NULL, 0);
+ CKERR(r);
+ dbt_init(&key, &k, sizeof k);
+ dbt_init(&val, &v, sizeof v);
+ r = loader->put(loader, &key, &val);
+ CKERR(r);
+ r = loader->close(loader);
+ CKERR(r);
+ assert_cmp_desc_valid(db);
+ });
+ assert_cmp_desc_valid(db);
+ CKERR(r);
+ do_inserts_and_queries(db);
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db->change_descriptor(db, txn_1, &other_desc, 0); CKERR(chk_r); }
+ assert_desc_eight(db);
+ assert_cmp_desc_valid(db);
+ });
+ assert_desc_eight(db);
+ assert_cmp_desc_valid(db);
+ do_inserts_and_queries(db);
+
+ IN_TXN_ABORT(env, NULL, txn_1, 0, {
+ { int chk_r = db->change_descriptor(db, txn_1, &orig_desc, 0); CKERR(chk_r); }
+ assert_desc_four(db);
+ assert_cmp_desc_valid(db);
+ });
+ assert_desc_eight(db);
+ assert_cmp_desc_valid(db);
+ do_inserts_and_queries(db);
+
+ {
+ int chk_r = db->close(db, 0); CKERR(chk_r);
+ cleanup();
+ open_env();
+ }
+
+ // verify that after close and reopen, cmp_descriptor is now
+ // latest descriptor
+ cmp_desc_is_four = false;
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ assert_desc_eight(db);
+ assert_cmp_desc_valid(db);
+ do_inserts_and_queries(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cmp_desc_is_four = true;
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_AUTO_COMMIT, 0666); CKERR(chk_r); }
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db->change_descriptor(db, txn_1, &orig_desc, DB_UPDATE_CMP_DESCRIPTOR); CKERR(chk_r); }
+ assert_desc_four(db);
+ assert_cmp_desc_valid(db);
+ });
+ assert_desc_four(db);
+ assert_cmp_desc_valid(db);
+ do_inserts_and_queries(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_compression_methods.cc b/storage/tokudb/PerconaFT/src/tests/test_compression_methods.cc
new file mode 100644
index 00000000000..56ca13f83fe
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_compression_methods.cc
@@ -0,0 +1,155 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/*
+ * Test that different compression methods can be used.
+ */
+
+#include <db.h>
+#include "test.h"
+
+static const int VAL_SIZE = 248;
+static const int NUM_ROWS = 1 << 12;
+
+static int
+insert(DB_ENV *env, DB *db, void *UU(extra))
+{
+ assert(VAL_SIZE%sizeof(int)==0);
+ int val[VAL_SIZE/sizeof(int)];
+ memset(val, 0, sizeof val);
+ DB_TXN *txn;
+ int r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+ for (int i = 0; i < NUM_ROWS; ++i) {
+ DBT k, v;
+ val[0] = i;
+ r = db->put(db, txn, dbt_init(&k, &i, sizeof i), dbt_init(&v, val, sizeof val), 0);
+ CKERR(r);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ return 0;
+}
+
+static int
+lookup(DB_ENV *env, DB *db, void *UU(extra))
+{
+ DB_TXN *txn;
+ int r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+ for (int i = 0; i < NUM_ROWS; ++i) {
+ DBT k, v;
+ r = db->get(db, txn, dbt_init(&k, &i, sizeof i), dbt_init(&v, NULL, 0), 0);
+ CKERR(r);
+ assert(v.size == (size_t) VAL_SIZE);
+ assert(*(int *) v.data == i);
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ return 0;
+}
+
+typedef int (*db_callback)(DB_ENV *env, DB *db, void *extra);
+static int
+with_open_db(db_callback cb, void *cb_extra, bool set_method, enum toku_compression_method method)
+{
+ DB_ENV *env;
+ DB *db;
+ int r;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+ if (set_method) {
+ r = db->set_compression_method(db, method);
+ CKERR(r);
+ }
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+
+ {
+ enum toku_compression_method saved_method;
+ r = db->get_compression_method(db, &saved_method);
+ CKERR(r);
+ assert(saved_method == method);
+ }
+
+ int cr = cb(env, db, cb_extra);
+
+ r = db->close(db, 0);
+ CKERR(r);
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return cr;
+}
+
+static void
+run_test(enum toku_compression_method method)
+{
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ r = with_open_db(insert, NULL, true, method);
+ CKERR(r);
+ r = with_open_db(lookup, NULL, false, method);
+ CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[])
+{
+ parse_args(argc, argv);
+ run_test(TOKU_NO_COMPRESSION);
+ run_test(TOKU_ZLIB_METHOD);
+ run_test(TOKU_ZLIB_WITHOUT_CHECKSUM_METHOD);
+ run_test(TOKU_QUICKLZ_METHOD);
+ run_test(TOKU_LZMA_METHOD);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_2.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_2.cc
new file mode 100644
index 00000000000..976114d0bf1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_2.cc
@@ -0,0 +1,127 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_cursor (void) {
+ if (verbose) printf("test_cursor\n");
+
+ DB_ENV * env;
+ DB *db;
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.cursor.ft_handle";
+ int r;
+
+ /* create the dup database file */
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_THREAD|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,stderr); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ int i;
+ int n = 42;
+ for (i=0; i<n; i++) {
+ int k = htonl(i);
+ int v = htonl(i);
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+ }
+
+ int ncursors = 2;
+ DBC *cursor[ncursors];
+ r = db->cursor(db, null_txn, &cursor[0], 0); assert(r == 0);
+ r = db->cursor(db, null_txn, &cursor[1], 0); assert(r == 0);
+
+ DBT k0; memset(&k0, 0, sizeof k0);
+ DBT v0; memset(&v0, 0, sizeof v0);
+ r = cursor[0]->c_get(cursor[0], &k0, &v0, DB_FIRST); assert(r == 0);
+ if (verbose) {
+ printf("k0:%p:%u\n", k0.data, k0.size);
+ printf("v0:%p:%u\n", v0.data, v0.size);
+ }
+
+ DBT k1; memset(&k1, 0, sizeof k1);
+ DBT v1; memset(&v1, 0, sizeof v1);
+ r = cursor[1]->c_get(cursor[1], &k1, &v1, DB_FIRST); assert(r == 0);
+ if (verbose) {
+ printf("k1:%p:%u\n", k1.data, k1.size);
+ printf("v1:%p:%u\n", v1.data, v1.size);
+ }
+
+ r = cursor[0]->c_get(cursor[0], &k0, &v0, DB_NEXT); assert(r == 0);
+ if (verbose) {
+ printf("k0:%p:%u\n", k0.data, k0.size);
+ printf("v0:%p:%u\n", v0.data, v0.size);
+ }
+
+ assert(k0.data != k1.data);
+ assert(v0.data != v1.data);
+
+ r = cursor[0]->c_close(cursor[0]); assert(r == 0);
+ r = cursor[1]->c_close(cursor[1]); assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ test_cursor();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_3.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_3.cc
new file mode 100644
index 00000000000..df7d1d652ac
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_3.cc
@@ -0,0 +1,139 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+// Verify that different cursors return different data items when DBT is given no flags.
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+verify_distinct_pointers (void **ptrs, int n) {
+ int i,j;
+ for (i=0; i<n; i++) {
+ for (j=i+1; j<n; j++) {
+ assert(ptrs[i]!=ptrs[j]);
+ }
+ }
+}
+
+DB_ENV * env;
+DB *db;
+DB_TXN * const null_txn = 0;
+
+enum { ncursors = 2 };
+DBC *cursor[ncursors];
+
+static void
+testit (uint32_t cop) {
+ void *kptrs[ncursors];
+ void *vptrs[ncursors];
+ int i;
+ for (i=0; i<ncursors; i++) {
+ DBT k0; memset(&k0, 0, sizeof k0);
+ DBT v0; memset(&v0, 0, sizeof v0);
+ int r = cursor[i]->c_get(cursor[i], &k0, &v0, cop);
+ CKERR(r);
+ kptrs[i] = k0.data;
+ vptrs[i] = v0.data;
+ }
+ verify_distinct_pointers(kptrs, ncursors);
+ verify_distinct_pointers(vptrs, ncursors);
+}
+
+static void
+test (void) {
+ if (verbose) printf("test_cursor\n");
+
+ const char * const fname = "test.cursor.ft_handle";
+ int r;
+
+ /* create the dup database file */
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_THREAD|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,stderr); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ int i;
+ int n = 42;
+ for (i=0; i<n; i++) {
+ int k = htonl(i);
+ int v = htonl(i);
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+ }
+
+ for (i=0; i<ncursors; i++) {
+ r = db->cursor(db, null_txn, &cursor[i], 0); CKERR(r);
+ }
+
+ testit(DB_FIRST);
+ testit(DB_NEXT);
+ testit(DB_PREV);
+ testit(DB_LAST);
+
+ r = cursor[0]->c_close(cursor[0]); assert(r == 0);
+ r = cursor[1]->c_close(cursor[1]); assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ test();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_DB_NEXT_no_dup.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_DB_NEXT_no_dup.cc
new file mode 100644
index 00000000000..8ccc27513b1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_DB_NEXT_no_dup.cc
@@ -0,0 +1,177 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static DBC* cursor = NULL;
+static DB* db = NULL;
+static DB_ENV* env = NULL;
+static int r = 0;
+static DB_TXN* null_txn = NULL;
+
+static void setup_env(void) {
+ assert(!env && !db && !cursor);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ assert(env);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_THREAD|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ assert(env);
+}
+
+static void close_env(void) {
+ assert(env && !db && !cursor);
+ r = env->close(env, 0);
+ CKERR(r);
+ env = NULL;
+}
+
+static void setup_db(void) {
+ assert(env && !db && !cursor);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ assert(db);
+ db->set_errfile(db, stderr);
+ r = db->open(db, null_txn, "foo.db", "main", DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ assert(db);
+}
+
+static void close_db(void) {
+ assert(env && db && !cursor);
+ r = db->close(db, 0);
+ CKERR(r);
+ db = NULL;
+}
+
+static void setup_cursor(void) {
+ assert(env && db && !cursor);
+ r = db->cursor(db, NULL, &cursor, 0);
+ CKERR(r);
+ assert(cursor);
+}
+
+static void close_cursor(void) {
+ assert(env && db && cursor);
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ cursor = NULL;
+}
+
+static void insert(char k, char d) {
+ DBT key;
+ DBT data;
+ r = db->put(db, null_txn, dbt_init(&key, &k, sizeof(k)), dbt_init(&data, &d, sizeof(d)), 0);
+ CKERR(r);
+}
+
+static void c_get(uint32_t flag, char key_expect, char data_expect) {
+ DBT key;
+ DBT data;
+
+ r = cursor->c_get(cursor, dbt_init(&key, 0, 0), dbt_init(&data, 0, 0), flag);
+ CKERR(r);
+ assert(key.size == sizeof(key_expect));
+ assert(data.size == sizeof(data_expect));
+ char got_key = *(char*)key.data;
+ char got_data = *(char*)data.data;
+ if (verbose &&
+ (got_key != key_expect || got_data != data_expect)) {
+ printf("c_get(%u) Expect (%c,%c)\n"
+ " Got (%c,%c)\n",
+ flag, key_expect, data_expect, got_key, got_data);
+ }
+ assert(got_key == key_expect);
+ assert(got_data == data_expect);
+}
+
+static void test_skip_key(uint32_t flag, bool is_next) {
+ setup_env();
+ setup_db();
+ setup_cursor();
+
+ /* ********************************************************************** */
+
+ char key = 'g';
+ char data = 'g';
+ int forward = is_next ? 1 : -1;
+
+ insert(key, data);
+ insert((char)(key + forward), data);
+ c_get(flag, key, data);
+ insert(key, (char)(data + forward));
+ c_get(flag, (char)(key + forward), data);
+
+ /* ********************************************************************** */
+ close_cursor();
+ close_db();
+ close_env();
+}
+
+static void run_test(void) {
+ /* ********************************************************************** */
+ /* Test DB_NEXT works properly. */
+ test_skip_key(DB_NEXT, true);
+ /* ********************************************************************** */
+ /* Test DB_PREV works properly. */
+ test_skip_key(DB_PREV, false);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+
+ run_test();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_db_current.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_db_current.cc
new file mode 100644
index 00000000000..00310b277e3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_db_current.cc
@@ -0,0 +1,162 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <memory.h>
+#include <db.h>
+
+static void
+db_put (DB *db, int k, int v) {
+ DB_TXN * const null_txn = 0;
+ DBT key, val;
+ int r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ CKERR(r);
+}
+
+static void
+test_cursor_current (void) {
+ if (verbose) printf("test_cursor_current\n");
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.cursor.current.ft_handle";
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ int k = 42, v = 42000;
+ db_put(db, k, v);
+ db_put(db, 43, 2000);
+
+ DBC *cursor;
+
+ r = db->cursor(db, null_txn, &cursor, 0); CKERR(r);
+
+ DBT key, data; int kk, vv;
+
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&data), DB_CURRENT);
+ assert(r == EINVAL);
+
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&data), DB_FIRST);
+ CKERR(r);
+ assert(key.size == sizeof kk);
+ memcpy(&kk, key.data, sizeof kk);
+ assert(kk == k);
+ assert(data.size == sizeof vv);
+ memcpy(&vv, data.data, data.size);
+ assert(vv == v);
+ toku_free(key.data); toku_free(data.data);
+
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&data), DB_CURRENT);
+ CKERR(r);
+ assert(key.size == sizeof kk);
+ memcpy(&kk, key.data, sizeof kk);
+ assert(kk == k);
+ assert(data.size == sizeof vv);
+ memcpy(&vv, data.data, data.size);
+ assert(vv == v);
+ r = db->del(db, null_txn, &key, DB_DELETE_ANY);
+ toku_free(key.data); toku_free(data.data);
+
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&data), DB_CURRENT);
+ CKERR2(r,DB_KEYEMPTY);
+
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&data), DB_CURRENT);
+ CKERR2(r,DB_KEYEMPTY);
+
+ r = cursor->c_close(cursor); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static void
+db_get (DB *db, int k, int UU(v), int expectr) {
+ DBT key, val;
+ int r = db->get(db, 0, dbt_init(&key, &k, sizeof k), dbt_init_malloc(&val), 0);
+ assert(r == expectr);
+}
+
+static void
+test_reopen (void) {
+ if (verbose) printf("test_reopen\n");
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.cursor.current.ft_handle";
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, 0, 0666); CKERR(r);
+
+ db_get(db, 1, 1, DB_NOTFOUND);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ test_cursor_current();
+ test_reopen();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_delete2.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_delete2.cc
new file mode 100644
index 00000000000..f976e1c6bc0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_delete2.cc
@@ -0,0 +1,109 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static DB_ENV *dbenv;
+static DB *db;
+static DB_TXN * txn;
+
+static void
+test_cursor_delete2 (void) {
+ int r;
+ DBT key,val;
+
+ r = db_env_create(&dbenv, 0); CKERR(r);
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, DB_PRIVATE|DB_INIT_MPOOL|DB_CREATE|DB_INIT_TXN, 0); CKERR(r);
+
+ r = db_create(&db, dbenv, 0); CKERR(r);
+ r = dbenv->txn_begin(dbenv, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "primary.db", NULL, DB_BTREE, DB_CREATE, 0600); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = dbenv->txn_begin(dbenv, 0, &txn, 0); CKERR(r);
+ r = db->put(db, txn, dbt_init(&key, "a", 2), dbt_init(&val, "b", 2), 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = dbenv->txn_begin(dbenv, 0, &txn, 0); CKERR(r);
+ r = db->del(db, txn, dbt_init(&key, "a", 2), 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = dbenv->txn_begin(dbenv, 0, &txn, 0); CKERR(r);
+ r = db->del(db, txn, dbt_init(&key, "a", 2), DB_DELETE_ANY); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = dbenv->txn_begin(dbenv, 0, &txn, 0); CKERR(r);
+ r = db->put(db, txn, dbt_init(&key, "a", 2), dbt_init(&val, "c", 2), 0); CKERR(r);
+ r = db->del(db, txn, dbt_init(&key, "a", 2), 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = dbenv->txn_begin(dbenv, 0, &txn, 0); CKERR(r);
+ r = db->put(db, txn, dbt_init(&key, "a", 2), dbt_init(&val, "c", 2), 0); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = dbenv->txn_begin(dbenv, 0, &txn, 0); CKERR(r);
+ r = db->del(db, txn, dbt_init(&key, "a", 2), 0); CKERR(r);
+ r = db->del(db, txn, dbt_init(&key, "a", 2), DB_DELETE_ANY); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = dbenv->close(dbenv, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ test_cursor_delete2();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_flags.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_flags.cc
new file mode 100644
index 00000000000..469c32bd4a5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_flags.cc
@@ -0,0 +1,92 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_cursor_flags (int cursor_flags, int expectr) {
+ if (verbose) printf("test_cursor_flags:%d %d\n", cursor_flags, expectr);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.cursor.delete.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ DBC *cursor;
+ r = db->cursor(db, null_txn, &cursor, cursor_flags);
+ assert(r == expectr);
+
+ if (r == 0) {
+ r = cursor->c_close(cursor); assert(r == 0);
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ test_cursor_flags(0, 0);
+ test_cursor_flags(~0, EINVAL);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_interrupt.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_interrupt.cc
new file mode 100644
index 00000000000..0020c968245
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_interrupt.cc
@@ -0,0 +1,152 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdio.h>
+
+#include <db.h>
+
+
+int num_interrupts_called;
+static bool interrupt(void* extra UU(), uint64_t rows UU()) {
+ num_interrupts_called++;
+ return false;
+}
+
+static bool interrupt_true(void* extra UU(), uint64_t rows UU()) {
+ num_interrupts_called++;
+ return true;
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *env;
+ DB *db;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->set_readpagesize(db, 1024);
+ CKERR(r);
+ r = db->set_pagesize(db, 1024*10);
+ CKERR(r);
+
+ const char * const fname = "test.change_pagesize";
+ r = db->open(db, NULL, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ DB_TXN* txn;
+ r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+ for (uint64_t i = 0; i < 10000; i++) {
+ DBT key, val;
+ uint64_t k = i;
+ uint64_t v = i;
+ dbt_init(&key, &k, sizeof k);
+ dbt_init(&val, &v, sizeof v);
+ db->put(db, txn, &key, &val, DB_PRELOCKED_WRITE); // adding DB_PRELOCKED_WRITE just to make the test go faster
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // create a snapshot txn so that when we delete the elements
+ // we just inserted, that they do not get garbage collected away
+ DB_TXN* snapshot_txn;
+ r = env->txn_begin(env, 0, &snapshot_txn, DB_TXN_SNAPSHOT);
+ CKERR(r);
+
+ DB_TXN* delete_txn;
+ r = env->txn_begin(env, 0, &delete_txn, DB_TXN_SNAPSHOT);
+ CKERR(r);
+
+ for (uint64_t i = 0; i < 10000; i++) {
+ DBT key;
+ uint64_t k = i;
+ dbt_init(&key, &k, sizeof k);
+ db->del(db, delete_txn, &key, DB_PRELOCKED_WRITE | DB_DELETE_ANY); // adding DB_PRELOCKED_WRITE just to make the test go faster
+ }
+ r = delete_txn->commit(delete_txn, 0);
+ CKERR(r);
+
+ // to make more than one basement node in the dictionary's leaf nodes
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ // create a txn that should see an empty dictionary
+ DB_TXN* test_txn;
+ r = env->txn_begin(env, 0, &test_txn, DB_TXN_SNAPSHOT);
+ CKERR(r);
+ DBC* cursor = NULL;
+ r = db->cursor(db, test_txn, &cursor, 0);
+ cursor->c_set_check_interrupt_callback(cursor, interrupt, NULL);
+ DBT key, val;
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ CKERR2(r, DB_NOTFOUND);
+ assert(num_interrupts_called > 1);
+ num_interrupts_called = 0;
+ cursor->c_set_check_interrupt_callback(cursor, interrupt_true, NULL);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ CKERR2(r, TOKUDB_INTERRUPTED);
+ assert(num_interrupts_called == 1);
+
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = test_txn->commit(test_txn, 0);
+ CKERR(r);
+
+
+ r = snapshot_txn->commit(snapshot_txn, 0);
+ CKERR(r);
+
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_nonleaf_expand.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_nonleaf_expand.cc
new file mode 100644
index 00000000000..29651c42e12
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_nonleaf_expand.cc
@@ -0,0 +1,142 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+expect_cursor_get (DBC *cursor, int k, int v, int op) {
+ int kk, vv;
+ DBT key, val;
+ int r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), op);
+ assert(r == 0);
+ assert(key.size == sizeof kk); memcpy(&kk, key.data, key.size); assert(kk == k); toku_free(key.data);
+ assert(val.size == sizeof vv); memcpy(&vv, val.data, val.size); assert(vv == v); toku_free(val.data);
+}
+
+static DBC *
+new_cursor (DB *db, int k, int v, int op) {
+ DBC *cursor;
+ int r;
+ r = db->cursor(db, 0, &cursor, 0); assert(r == 0);
+ expect_cursor_get(cursor, k, v, op);
+ return cursor;
+}
+
+static int
+db_put (DB *db, int k, int v) {
+ DBT key, val;
+ int r = db->put(db, 0, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ return r;
+}
+
+/* use inserts and cursors to test the ft_nonleaf_expand function
+ insert keys 0 and n and set cursors to them
+ then insert keys 1 .. n-1. this should cause leaf splits, new root nodes, nonleaf expands
+ and nonleaf splits as the tree grows.
+
+ the reverse parameter controls where in insertions are made to test the <, =, >
+ cases in the ft_nonleaf_expand function */
+
+static void
+test_cursor_nonleaf_expand (int n, int reverse) {
+ if (verbose) printf("test_cursor_nonleaf_expand:%d %d\n", n, reverse);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.insert.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_pagesize(db, 4096); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ r = db_put(db, htonl(0), 0); assert(r == 0);
+ DBC *cursor0 = new_cursor(db, htonl(0), 0, DB_FIRST); assert(cursor0);
+ r = db_put(db, htonl(n), n); assert(r == 0);
+ DBC *cursorn = new_cursor(db, htonl(n), n, DB_LAST); assert(cursorn);
+
+ int i;
+ if (reverse) {
+ for (i=n-1; i > 0; i--) {
+ r = db_put(db, htonl(i), i); assert(r == 0);
+ }
+ } else {
+ for (i=1; i < n; i++) {
+ r = db_put(db, htonl(i), i); assert(r == 0);
+ }
+ }
+
+ /* make sure the cursors did not move */
+ expect_cursor_get(cursor0, htonl(0), 0, DB_CURRENT);
+ expect_cursor_get(cursorn, htonl(n), n, DB_CURRENT);
+
+ r = cursor0->c_close(cursor0); assert(r == 0);
+ r = cursorn->c_close(cursorn); assert(r == 0);
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int i;
+ for (i=1; i<=65536; i *= 2) {
+ test_cursor_nonleaf_expand(i, 0);
+ test_cursor_nonleaf_expand(i, 1);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_null.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_null.cc
new file mode 100644
index 00000000000..e106974a206
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_null.cc
@@ -0,0 +1,210 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <memory.h>
+#include <db.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB *db;
+DB_ENV* dbenv;
+DBC* cursors[(int)256];
+DB_TXN* null_txn = NULL;
+
+static void
+put (int _key, int _data) {
+ int r;
+ DBT key;
+ DBT data;
+ dbt_init(&key, &_key, sizeof(int));
+ dbt_init(&data, &_data, sizeof(int));
+ if (_key == -1) {
+ key.data = NULL;
+ key.size = 0;
+ }
+ if (_data == -1) {
+ data.data = NULL;
+ data.size = 0;
+ }
+
+ r = db->put(db, null_txn, &key, &data, 0);
+ CKERR(r);
+}
+
+static void
+cget (uint32_t flag, bool find, char txn, int _key, int _data) {
+ assert(cursors[(int)txn]);
+
+ int r;
+ DBT key;
+ DBT data;
+ if (flag == DB_CURRENT) {
+ _key++;
+ _data++;
+ dbt_init(&key, &_key, sizeof(int));
+ dbt_init(&data, &_data, sizeof(int));
+ _key--;
+ _data--;
+ }
+ else if (flag == DB_SET) {
+ dbt_init(&key, &_key, sizeof(int));
+ if (_key == -1) {
+ key.data = NULL;
+ key.size = 0;
+ }
+ _data++;
+ dbt_init(&data, &_data, sizeof(int));
+ _data--;
+ }
+ else assert(false);
+ r = cursors[(int)txn]->c_get(cursors[(int)txn], &key, &data, flag);
+ if (find) {
+ CKERR(r);
+ if (_key == -1) {
+ assert(key.data == NULL);
+ assert(key.size == 0);
+ }
+ else {
+ assert(key.size == sizeof(int));
+ assert(*(int*)key.data == _key);
+ }
+ if (_data == -1) {
+ assert(data.data == NULL);
+ assert(data.size == 0);
+ }
+ else {
+ assert(data.size == sizeof(int));
+ assert(*(int*)data.data == _data);
+ }
+ }
+ else CKERR2(r, DB_NOTFOUND);
+}
+
+static void
+init_dbc (char name) {
+ int r;
+
+ assert(!cursors[(int)name]);
+ r = db->cursor(db, null_txn, &cursors[(int)name], 0);
+ CKERR(r);
+ assert(cursors[(int)name]);
+}
+
+static void
+close_dbc (char name) {
+ int r;
+
+ assert(cursors[(int)name]);
+ r = cursors[(int)name]->c_close(cursors[(int)name]);
+ CKERR(r);
+ cursors[(int)name] = NULL;
+}
+
+static void
+setup_dbs (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ dbenv = NULL;
+ db = NULL;
+ /* Open/create primary */
+ r = db_env_create(&dbenv, 0);
+ CKERR(r);
+ uint32_t env_txn_flags = 0;
+ uint32_t env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL;
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, env_open_flags | env_txn_flags, 0600);
+ CKERR(r);
+
+ r = db_create(&db, dbenv, 0);
+ CKERR(r);
+
+ char a;
+ r = db->open(db, null_txn, "foobar.db", NULL, DB_BTREE, DB_CREATE, 0600);
+ CKERR(r);
+ for (a = 'a'; a <= 'z'; a++) init_dbc(a);
+}
+
+static void
+close_dbs (void) {
+ char a;
+ for (a = 'a'; a <= 'z'; a++) {
+ if (cursors[(int)a]) close_dbc(a);
+ }
+
+ int r;
+ r = db->close(db, 0);
+ CKERR(r);
+ db = NULL;
+ r = dbenv->close(dbenv, 0);
+ CKERR(r);
+ dbenv = NULL;
+}
+
+static void
+test (void) {
+ /* ********************************************************************** */
+ int key;
+ int data;
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (i & 0x1) key = -1;
+ else key = 1;
+ if (i & 0x2) data = -1;
+ else data = 1;
+ setup_dbs();
+ put(key, data);
+ cget(DB_SET, true, 'a', key, data);
+ cget(DB_CURRENT, true, 'a', key, data);
+ close_dbs();
+ }
+ /* ********************************************************************** */
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_stickyness.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_stickyness.cc
new file mode 100644
index 00000000000..b271e1759fa
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_stickyness.cc
@@ -0,0 +1,133 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+
+static void
+db_put (DB *db, int k, int v) {
+ DB_TXN * const null_txn = 0;
+ DBT key, val;
+ int r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+}
+
+static int
+cursor_get (DBC *cursor, unsigned int *k, unsigned int *v, int op) {
+ DBT key, val;
+ int r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), op);
+ if (r == 0) {
+ assert(key.size == sizeof *k); memcpy(k, key.data, key.size);
+ assert(val.size == sizeof *v); memcpy(v, val.data, val.size);
+ }
+ if (key.data) toku_free(key.data);
+ if (val.data) toku_free(val.data);
+ return r;
+}
+
+static void
+test_cursor_sticky (int n, int dup_mode) {
+ if (verbose) printf("test_cursor_sticky:%d %d\n", n, dup_mode);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test_cursor_sticky.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_flags(db, dup_mode); assert(r == 0);
+ r = db->set_pagesize(db, 4096); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ int i;
+ unsigned int k, v;
+ for (i=0; i<n; i++) {
+ db_put(db, htonl(i), htonl(i));
+ }
+
+ /* walk the tree */
+ DBC *cursor;
+ r = db->cursor(db, 0, &cursor, 0); assert(r == 0);
+ for (i=0; i<n; i++) {
+ // GCC 4.8 complains about these being maybe uninitialized.
+ // TODO(leif): figure out why and fix it.
+ k = 0; v = 0;
+ r = cursor_get(cursor, &k, &v, DB_NEXT); assert(r == 0);
+ assert(k == htonl(i)); assert(v == htonl(i));
+ }
+
+ r = cursor_get(cursor, &k, &v, DB_NEXT); assert(r == DB_NOTFOUND);
+
+ r = cursor_get(cursor, &k, &v, DB_CURRENT); assert(r == 0); assert(k == htonl(n-1)); assert(v == htonl(n-1));
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ int i;
+
+ // setvbuf(stdout, NULL, _IONBF, 0);
+ parse_args(argc, argv);
+
+ for (i=1; i<65537; i *= 2) {
+ test_cursor_sticky(i, 0);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_cursor_with_read_txn.cc b/storage/tokudb/PerconaFT/src/tests/test_cursor_with_read_txn.cc
new file mode 100644
index 00000000000..449be7181b1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_cursor_with_read_txn.cc
@@ -0,0 +1,125 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+int test_main(int argc, char * const argv[])
+{
+ int r;
+ DB * db;
+ DB_ENV * env;
+ (void) argc;
+ (void) argv;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); { int chk_r = r; CKERR(chk_r); }
+
+ // set things up
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, 0755);
+ CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_CREATE, 0644);
+ CKERR(r);
+
+
+ DB_TXN* txn = NULL;
+ r = env->txn_begin(env, 0, &txn, DB_TXN_SNAPSHOT);
+ CKERR(r);
+
+ int k = 1;
+ int v = 10;
+ DBT key, val;
+ r = db->put(
+ db,
+ txn,
+ dbt_init(&key, &k, sizeof k),
+ dbt_init(&val, &v, sizeof v),
+ 0
+ );
+ CKERR(r);
+ k = 2;
+ v = 20;
+ r = db->put(
+ db,
+ txn,
+ dbt_init(&key, &k, sizeof k),
+ dbt_init(&val, &v, sizeof v),
+ 0
+ );
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = env->txn_begin(env, 0, &txn, DB_TXN_SNAPSHOT | DB_TXN_READ_ONLY);
+ CKERR(r);
+ DBC* cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0);
+ CKERR(r);
+ DBT key1, val1;
+ memset(&key1, 0, sizeof key1);
+ memset(&val1, 0, sizeof val1);
+ r = cursor->c_get(cursor, &key1, &val1, DB_FIRST);
+ CKERR(r);
+ invariant(key1.size == sizeof(int));
+ invariant(*(int *)key1.data == 1);
+ invariant(val1.size == sizeof(int));
+ invariant(*(int *)val1.data == 10);
+
+ r = cursor->c_get(cursor, &key1, &val1, DB_NEXT);
+ CKERR(r);
+ invariant(key1.size == sizeof(int));
+ invariant(*(int *)key1.data == 2);
+ invariant(val1.size == sizeof(int));
+ invariant(*(int *)val1.data == 20);
+
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // clean things up
+ r = db->close(db, 0);
+ CKERR(r);
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_already_exists.cc b/storage/tokudb/PerconaFT/src/tests/test_db_already_exists.cc
new file mode 100644
index 00000000000..68b162968ab
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_already_exists.cc
@@ -0,0 +1,97 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <db.h>
+#include <errno.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.already.exists.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ // r = db->set_flags(db, DB_DUP); CKERR(r);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, 0, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_EXCL, 0666);
+ assert(r == EINVAL);
+
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ db->set_errfile(db,0); // Turn off those annoying errors
+
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE | DB_EXCL, 0666);
+ assert(r == EEXIST);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_change_pagesize.cc b/storage/tokudb/PerconaFT/src/tests/test_db_change_pagesize.cc
new file mode 100644
index 00000000000..ea576a154b6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_change_pagesize.cc
@@ -0,0 +1,104 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdio.h>
+
+#include <db.h>
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *env;
+ DB *db;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE | DB_INIT_LOG, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->set_pagesize(db, 10000);
+ CKERR(r);
+
+ const char * const fname = "test.change_pagesize";
+ r = db->open(db, NULL, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ DB_TXN* txn;
+ r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+ for (uint64_t i = 0; i < 10000; i++) {
+ DBT key, val;
+ uint64_t k = i;
+ uint64_t v = i;
+ dbt_init(&key, &k, sizeof k);
+ dbt_init(&val, &v, sizeof v);
+ db->put(db, txn, &key, &val, DB_PRELOCKED_WRITE); // adding DB_PRELOCKED_WRITE just to make the test go faster
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // now we change the pagesize. In 6.1.0, this would eventually cause a crash
+ r = db->change_pagesize(db, 1024);
+ CKERR(r);
+
+ r = env->txn_begin(env, 0, &txn, 0);
+ CKERR(r);
+ for (uint64_t i = 0; i < 10000; i++) {
+ DBT key, val;
+ uint64_t k = 10000+i;
+ uint64_t v = i;
+ dbt_init(&key, &k, sizeof k);
+ dbt_init(&val, &v, sizeof v);
+ db->put(db, txn, &key, &val, DB_PRELOCKED_WRITE); // adding DB_PRELOCKED_WRITE just to make the test go faster
+ }
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_change_xxx.cc b/storage/tokudb/PerconaFT/src/tests/test_db_change_xxx.cc
new file mode 100644
index 00000000000..1310d9d306d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_change_xxx.cc
@@ -0,0 +1,150 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Can I close a db without opening it? */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ DB_ENV *env;
+ DB *db;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ uint32_t ret_val = 0;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->set_pagesize(db, 112024);
+ CKERR(r);
+ r = db->change_pagesize(db, 202433);
+ CKERR2(r, EINVAL);
+ r = db->get_pagesize(db, &ret_val);
+ CKERR(r);
+ assert(ret_val == 112024);
+ r = db->set_readpagesize(db, 33024);
+ CKERR(r);
+ r = db->change_readpagesize(db, 202433);
+ CKERR2(r, EINVAL);
+ r = db->get_readpagesize(db, &ret_val);
+ CKERR(r);
+ assert(ret_val == 33024);
+
+ enum toku_compression_method method = TOKU_ZLIB_METHOD;
+ enum toku_compression_method ret_method = TOKU_NO_COMPRESSION;
+ r = db->set_compression_method(db, method);
+ CKERR(r);
+ r = db->change_compression_method(db, method);
+ CKERR2(r, EINVAL);
+ r = db->get_compression_method(db, &ret_method);
+ CKERR(r);
+ assert(ret_method == TOKU_ZLIB_METHOD);
+
+ // now do the open
+ const char * const fname = "test.change_xxx";
+ r = db->open(db, NULL, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+
+ r = db->get_pagesize(db, &ret_val);
+ CKERR(r);
+ assert(ret_val == 112024);
+ r = db->get_readpagesize(db, &ret_val);
+ CKERR(r);
+ assert(ret_val == 33024);
+ ret_method = TOKU_NO_COMPRESSION;
+ r = db->get_compression_method(db, &ret_method);
+ CKERR(r);
+ assert(ret_method == TOKU_ZLIB_METHOD);
+
+ r = db->set_pagesize(db, 2024);
+ CKERR2(r, EINVAL);
+ r = db->set_readpagesize(db, 1111);
+ CKERR2(r, EINVAL);
+ r = db->set_compression_method(db, TOKU_NO_COMPRESSION);
+ CKERR2(r, EINVAL);
+
+ r = db->change_pagesize(db, 100000);
+ CKERR(r);
+ r = db->change_readpagesize(db, 10000);
+ CKERR(r);
+ r = db->change_compression_method(db, TOKU_LZMA_METHOD);
+ CKERR(r);
+
+ r = db->get_pagesize(db, &ret_val);
+ CKERR(r);
+ assert(ret_val == 100000);
+ r = db->get_readpagesize(db, &ret_val);
+ CKERR(r);
+ assert(ret_val == 10000);
+ ret_method = TOKU_NO_COMPRESSION;
+ r = db->get_compression_method(db, &ret_method);
+ CKERR(r);
+ assert(ret_method == TOKU_LZMA_METHOD);
+
+ r = db->close(db, 0);
+
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, NULL, fname, "main", DB_BTREE, DB_AUTO_COMMIT, 0666);
+ CKERR(r);
+
+ r = db->get_pagesize(db, &ret_val);
+ CKERR(r);
+ assert(ret_val == 100000);
+ r = db->get_readpagesize(db, &ret_val);
+ CKERR(r);
+ assert(ret_val == 10000);
+ ret_method = TOKU_NO_COMPRESSION;
+ r = db->get_compression_method(db, &ret_method);
+ CKERR(r);
+ assert(ret_method == TOKU_LZMA_METHOD);
+
+ r = db->close(db, 0);
+
+ r=env->close(env, 0); assert(r==0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_close_no_open.cc b/storage/tokudb/PerconaFT/src/tests/test_db_close_no_open.cc
new file mode 100644
index 00000000000..dc2d32cea72
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_close_no_open.cc
@@ -0,0 +1,65 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Can I close a db without opening it? */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+DB *db;
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_create(&db, env, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_current_clobbers_db.cc b/storage/tokudb/PerconaFT/src/tests/test_db_current_clobbers_db.cc
new file mode 100644
index 00000000000..80cfd0442e6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_current_clobbers_db.cc
@@ -0,0 +1,109 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* DB_CURRENT */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+DB *db;
+DB_TXN* null_txn = NULL;
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_env_create(&env, 0); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_PRIVATE|DB_INIT_MPOOL|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, "foo.db", "main", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, null_txn, &cursor, 0); CKERR(r);
+ DBT key, val;
+ DBT ckey, cval;
+ int k1 = 1, v1=7;
+ enum foo { blob = 1 };
+ int k2 = 2;
+ int v2 = 8;
+ r = db->put(db, null_txn, dbt_init(&key, &k1, sizeof(k1)), dbt_init(&val, &v1, sizeof(v1)), 0);
+ CKERR(r);
+ r = db->put(db, null_txn, dbt_init(&key, &k2, sizeof(k2)), dbt_init(&val, &v2, sizeof(v2)), 0);
+ CKERR(r);
+
+ r = cursor->c_get(cursor, dbt_init(&ckey, NULL, 0), dbt_init(&cval, NULL, 0), DB_LAST);
+ CKERR(r);
+ //Copies a static pointer into val.
+ r = db->get(db, null_txn, dbt_init(&key, &k1, sizeof(k1)), dbt_init(&val, NULL, 0), 0);
+ CKERR(r);
+ assert(val.data != &v1);
+ assert(*(int*)val.data == v1);
+
+ r = cursor->c_get(cursor, dbt_init(&ckey, NULL, 0), dbt_init(&cval, NULL, 0), DB_LAST);
+ CKERR(r);
+
+ //Does not corrupt it.
+ assert(val.data != &v1);
+ assert(*(int*)val.data == v1);
+
+ r = cursor->c_get(cursor, &ckey, &cval, DB_CURRENT);
+ CKERR(r);
+
+ assert(*(int*)val.data == v1); // Will bring up valgrind error.
+
+
+ r = db->del(db, null_txn, &ckey, DB_DELETE_ANY); assert(r == 0);
+ CKERR(r);
+
+ assert(*(int*)val.data == v1); // Will bring up valgrind error.
+
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_dbt_mem_behavior.cc b/storage/tokudb/PerconaFT/src/tests/test_db_dbt_mem_behavior.cc
new file mode 100644
index 00000000000..cf747e11348
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_dbt_mem_behavior.cc
@@ -0,0 +1,192 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <memory.h>
+#include <db.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+typedef struct {
+ int32_t pkey;
+ char waste[1024];
+} DATA;
+
+DB* db;
+DB_TXN *const null_txn = 0;
+DB_ENV *dbenv;
+uint32_t set_ulen;
+int32_t key_1 = 1;
+
+static void
+setup(void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&dbenv, 0); assert(r == 0);
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+ /* Open/create primary */
+ r = db_create(&db, dbenv, 0); CKERR(r);
+ r = db->open(db, null_txn, "primary.db", NULL, DB_BTREE, DB_CREATE, 0600); CKERR(r);
+}
+
+static void
+insert_test (void) {
+ int r;
+ DATA entry;
+ DBT data;
+ DBT key;
+
+ memset(&entry, 0xFF, sizeof(entry));
+ entry.pkey = key_1;
+
+ dbt_init(&key, &entry.pkey, sizeof(entry.pkey));
+ dbt_init(&data, &entry, sizeof(entry));
+ r = db->put(db, null_txn, &key, &data, 0); CKERR(r);
+}
+
+static void
+close_dbs (void) {
+ int r;
+
+ r = db->close(db, 0); CKERR(r);
+ r = dbenv->close(dbenv, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ int i;
+ int r;
+
+ parse_args(argc, argv);
+//Simple flags that require minimal setup.
+ uint32_t flags[] = {
+ 0,
+ DB_DBT_USERMEM,
+ DB_DBT_MALLOC,
+ DB_DBT_REALLOC,
+ };
+ int num_flags = sizeof(flags) / sizeof(flags[0]);
+
+ int j;
+ setup();
+ insert_test();
+ DBT key;
+ DBT data;
+ void* oldmem;
+
+ for (j = 0; j < num_flags; j++) {
+ for (i = 0; i < 2; i++) {
+ if (i) set_ulen = sizeof(DATA) / 2;
+ else set_ulen = sizeof(DATA);
+
+ unsigned int old_ulen;
+ int was_truncated = 0;
+ int ulen_changed;
+ int size_full;
+ int doclone = 0;
+ DATA fake;
+ int small_buffer = 0;
+
+ memset(&fake, 0xFF, sizeof(DATA));
+ fake.pkey = key_1;
+
+
+ dbt_init(&key, &key_1, sizeof(key_1));
+ dbt_init(&data, 0, 0);
+ data.flags = flags[j];
+ oldmem = toku_malloc(set_ulen);
+ data.data = oldmem;
+ memset(oldmem, 0, set_ulen);
+ if (flags[j] == DB_DBT_USERMEM) {
+ data.ulen = set_ulen;
+ }
+ old_ulen = data.ulen;
+ r = db->get(db, null_txn, &key, &data, 0);
+ if (flags[j] == DB_DBT_USERMEM && set_ulen < sizeof(DATA)) CKERR2(r, DB_BUFFER_SMALL);
+ else CKERR(r);
+
+ if (r == DB_BUFFER_SMALL) {
+ //The entire 'waste' is full of 0xFFs
+ DATA* CAST_FROM_VOIDP(entry, data.data);
+ was_truncated = entry->waste[0] != 0;
+ small_buffer = 1;
+ }
+ ulen_changed = data.ulen != old_ulen;
+ size_full = data.size == sizeof(DATA);
+
+ unsigned int min = data.ulen < data.size ? data.ulen : data.size;
+ min = min < sizeof(DATA) ? min : sizeof(DATA);
+ //assert(min == sizeof(DATA));
+ r = memcmp((DATA*)data.data, &fake, min);
+ doclone = r == 0;
+
+ if (flags[j] != 0) {
+ toku_free(data.data);
+ }
+ if (flags[j] == 0 || flags[j] == DB_DBT_MALLOC) {
+ toku_free(oldmem);
+ }
+
+ assert(!was_truncated);
+
+ bool ulen_should_change = false;
+ if (flags[j] == DB_DBT_REALLOC) {
+ ulen_should_change = (bool)(old_ulen < sizeof(DATA));
+ }
+ else if (flags[j] == DB_DBT_MALLOC) {
+ ulen_should_change = (bool)(old_ulen != sizeof(DATA)*2);
+ }
+ assert(ulen_should_change == (bool)ulen_changed);
+ assert(size_full);
+ assert(doclone == !small_buffer);
+ }
+ }
+ oldmem = 0;
+ dbt_init(&key, 0, 0);
+ dbt_init(&data, 0, 0);
+ close_dbs();
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_delete.cc b/storage/tokudb/PerconaFT/src/tests/test_db_delete.cc
new file mode 100644
index 00000000000..36aaa197f53
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_delete.cc
@@ -0,0 +1,186 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+
+
+static void
+db_put (DB *db, int k, int v) {
+ DBT key, val;
+ int r = db->put(db, 0, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+}
+
+static void
+expect_db_del (DB *db, int k, int flags, int expectr) {
+ DBT key;
+ int r = db->del(db, 0, dbt_init(&key, &k, sizeof k), flags);
+ assert(r == expectr);
+}
+
+static void
+expect_db_get (DB *db, int k, int expectr) {
+ DBT key, val;
+ int r = db->get(db, 0, dbt_init(&key, &k, sizeof k), dbt_init_malloc(&val), 0);
+ assert(r == expectr);
+}
+
+static void
+test_db_delete (int n, int dup_mode) {
+ if (verbose) printf("test_db_delete:%d %d\n", n, dup_mode);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.db.delete.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->set_redzone(env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_flags(db, dup_mode);
+ assert(r == 0);
+ r = db->set_pagesize(db, 4096);
+ assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ /* insert n/2 <i, i> pairs */
+ int i;
+ for (i=0; i<n/2; i++)
+ db_put(db, htonl(i), i);
+
+ /* reopen the database to force nonleaf buffering */
+ r = db->close(db, 0);
+ assert(r == 0);
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_flags(db, dup_mode);
+ assert(r == 0);
+ r = db->set_pagesize(db, 4096);
+ assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, 0, 0666);
+ assert(r == 0);
+
+ /* insert n/2 <i, i> pairs */
+ for (i=n/2; i<n; i++)
+ db_put(db, htonl(i), i);
+
+ for (i=0; i<n; i++) {
+ expect_db_del(db, htonl(i), 0, 0);
+
+ expect_db_get(db, htonl(i), DB_NOTFOUND);
+ }
+
+ expect_db_del(db, htonl(n), 0, DB_NOTFOUND);
+ expect_db_del(db, htonl(n), DB_DELETE_ANY, 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+static void
+test_db_get_datasize0 (void) {
+ if (verbose) printf("test_db_get_datasize0\n");
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.db_delete.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->set_redzone(env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_pagesize(db, 4096);
+ assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ int k = 0;
+ db_put(db, k, 0);
+
+ DBT key, val;
+ r = db->get(db, 0, dbt_init(&key, &k, sizeof k), dbt_init_malloc(&val), 0);
+ assert(r == 0);
+ toku_free(val.data);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ test_db_get_datasize0();
+
+ test_db_delete(0, 0);
+
+ int i;
+ for (i = 1; i <= (1<<16); i *= 2) {
+ test_db_delete(i, 0);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_descriptor.cc b/storage/tokudb/PerconaFT/src/tests/test_db_descriptor.cc
new file mode 100644
index 00000000000..60971315421
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_descriptor.cc
@@ -0,0 +1,336 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <toku_portability.h>
+#include <memory.h>
+#include <toku_portability.h>
+#include <db.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "test.h"
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+#define FNAME "foo.tokudb"
+const char *name = NULL;
+
+#define NUM 3
+#define MAX_LENGTH (1<<16)
+
+int order[NUM+1];
+uint32_t length[NUM];
+uint8_t data[NUM][MAX_LENGTH];
+DBT descriptors[NUM];
+DB_ENV *env;
+
+enum {NUM_DBS=2};
+DB *dbs[NUM_DBS];
+DB_TXN *txn = NULL;
+DB_TXN *null_txn;
+int last_open_descriptor = -1;
+
+int abort_type;
+int get_table_lock;
+uint64_t num_called = 0;
+
+
+static void
+verify_db_matches(void) {
+ DB *db;
+ int which;
+ for (which = 0; which < NUM_DBS; which++) {
+ db = dbs[which];
+ if (db) {
+ const DBT * dbt = &db->descriptor->dbt;
+
+ if (last_open_descriptor<0) {
+ assert(dbt->size == 0 && dbt->data == NULL);
+ }
+ else {
+ assert(last_open_descriptor < NUM);
+ assert(dbt->size == descriptors[last_open_descriptor].size);
+ assert(!memcmp(dbt->data, descriptors[last_open_descriptor].data, dbt->size));
+ assert(dbt->data != descriptors[last_open_descriptor].data);
+ }
+ }
+ }
+
+}
+
+static int
+verify_int_cmp (DB *dbp, const DBT *a, const DBT *b) {
+ num_called++;
+ verify_db_matches();
+ int r = int_dbt_cmp(dbp, a, b);
+ return r;
+}
+
+static void
+open_db(int descriptor, int which) {
+ /* create the dup database file */
+ assert(dbs[which]==NULL);
+ DB *db;
+ int r = db_create(&db, env, 0);
+ CKERR(r);
+ dbs[which] = db;
+
+ assert(abort_type >=0 && abort_type <= 2);
+ if (abort_type==2 && !txn) {
+ r = env->txn_begin(env, null_txn, &txn, 0);
+ CKERR(r);
+ last_open_descriptor = -1; //DB was destroyed at end of last close, did not hang around.
+ }
+ r = db->open(db, txn, FNAME, name, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ if (descriptor >= 0) {
+ assert(descriptor < NUM);
+ if (txn) {
+ { int chk_r = db->change_descriptor(db, txn, &descriptors[descriptor], 0); CKERR(chk_r); }
+ }
+ else {
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db->change_descriptor(db, txn_desc, &descriptors[descriptor], 0); CKERR(chk_r); }
+ });
+ }
+ last_open_descriptor = descriptor;
+ }
+ verify_db_matches();
+ if (abort_type!=2 && !txn) {
+ r = env->txn_begin(env, null_txn, &txn, 0);
+ CKERR(r);
+ }
+ assert(txn);
+ if (get_table_lock) {
+ r = db->pre_acquire_table_lock(db, txn);
+ CKERR(r);
+ }
+}
+
+static void
+delete_db(void) {
+ int which;
+ for (which = 0; which < NUM_DBS; which++) {
+ assert(dbs[which] == NULL);
+ }
+ int r = env->dbremove(env, NULL, FNAME, name, 0);
+ if (abort_type==2) {
+ CKERR2(r, ENOENT); //Abort deleted it
+ }
+ else CKERR(r);
+ last_open_descriptor = -1;
+}
+
+static void
+close_db(int which) {
+ assert(dbs[which]!=NULL);
+ DB *db = dbs[which];
+ dbs[which] = NULL;
+
+ int r;
+ if (which==1) {
+ r = db->close(db, 0);
+ CKERR(r);
+ return;
+ }
+ if (abort_type>0) {
+ if (abort_type==2 && dbs[1]) {
+ close_db(1);
+ }
+ r = db->close(db, 0);
+ CKERR(r);
+ r = txn->abort(txn);
+ CKERR(r);
+ }
+ else {
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+ }
+ txn = NULL;
+}
+
+static void
+setup_data(void) {
+ int r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, verify_int_cmp); CKERR(r);
+ const int envflags = DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK |DB_THREAD |DB_PRIVATE;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ int i;
+ for (i=0; i < NUM; i++) {
+ length[i] = i * MAX_LENGTH / (NUM-1);
+ uint32_t j;
+ for (j = 0; j < length[i]; j++) {
+ data[i][j] = (uint8_t)(random() & 0xFF);
+ }
+ memset(&descriptors[i], 0, sizeof(descriptors[i]));
+ descriptors[i].size = length[i];
+ descriptors[i].data = &data[i][0];
+ }
+ last_open_descriptor = -1;
+ txn = NULL;
+}
+
+static void
+permute_order(void) {
+ int i;
+ for (i=0; i < NUM; i++) {
+ order[i] = i;
+ }
+ for (i=0; i < NUM; i++) {
+ int which = (random() % (NUM-i)) + i;
+ int temp = order[i];
+ order[i] = order[which];
+ order[which] = temp;
+ }
+}
+
+static void
+test_insert (int n, int which) {
+ if (which == -1) {
+ for (which = 0; which < NUM_DBS; which++) {
+ if (dbs[which]) {
+ test_insert(n, which);
+ }
+ }
+ return;
+ }
+ assert(dbs[which]!=NULL);
+ DB *db = dbs[which];
+ int i;
+ static int last = 0;
+ for (i=0; i<n; i++) {
+ int k = last++;
+ DBT key, val;
+ uint64_t called = num_called;
+ int r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ if (i>0) assert(num_called > called);
+ CKERR(r);
+ }
+}
+
+
+static void
+runtest(void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ setup_data();
+ permute_order();
+
+ int i;
+ /* Subsumed by rest of test.
+ for (i=0; i < NUM; i++) {
+ open_db(-1, 0);
+ test_insert(i, 0);
+ close_db(0);
+ open_db(-1, 0);
+ test_insert(i, 0);
+ close_db(0);
+ delete_db();
+ }
+
+ for (i=0; i < NUM; i++) {
+ open_db(order[i], 0);
+ test_insert(i, 0);
+ close_db(0);
+ open_db(-1, 0);
+ test_insert(i, 0);
+ close_db(0);
+ open_db(order[i], 0);
+ test_insert(i, 0);
+ close_db(0);
+ delete_db();
+ }
+ */
+
+ //Upgrade descriptors along the way. Need version to increase, so do not use 'order[i]'
+ for (i=0; i < NUM; i++) {
+ open_db(i, 0);
+ test_insert(i, 0);
+ close_db(0);
+ open_db(-1, 0);
+ test_insert(i, 0);
+ close_db(0);
+ open_db(i, 0);
+ test_insert(i, 0);
+ close_db(0);
+ }
+ delete_db();
+
+ //Upgrade descriptors along the way. With two handles
+ open_db(-1, 1);
+ for (i=0; i < NUM; i++) {
+ open_db(i, 0);
+ test_insert(i, -1);
+ close_db(0);
+ open_db(-1, 0);
+ test_insert(i, -1);
+ close_db(0);
+ open_db(i, 0);
+ test_insert(i, -1);
+ close_db(0);
+ }
+ if (dbs[1]) {
+ close_db(1);
+ }
+ delete_db();
+
+ env->close(env, 0);
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ for (abort_type = 0; abort_type < 3; abort_type++) {
+ for (get_table_lock = 0; get_table_lock < 2; get_table_lock++) {
+ name = NULL;
+ runtest();
+
+ name = "bar";
+ runtest();
+
+ }
+ }
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_env_open_close.cc b/storage/tokudb/PerconaFT/src/tests/test_db_env_open_close.cc
new file mode 100644
index 00000000000..e8a641e376a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_env_open_close.cc
@@ -0,0 +1,59 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdio.h>
+
+#include <db.h>
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *dbenv;
+ int r;
+
+ r = db_env_create(&dbenv, 0);
+ assert(r == 0);
+
+ r = dbenv->close(dbenv, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_env_open_nocreate.cc b/storage/tokudb/PerconaFT/src/tests/test_db_env_open_nocreate.cc
new file mode 100644
index 00000000000..aebd3cb2393
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_env_open_nocreate.cc
@@ -0,0 +1,92 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+// Try to open an environment where the directory does not exist
+// Try when the dir exists but is not an initialized env
+// Try when the dir exists and we do DB_CREATE: it should work.
+// And after that the open should work without a DB_CREATE
+// However, in BDB, after doing an DB_ENV->open and then a close, no state has changed
+// One must actually create a DB I think...
+
+
+#include <db.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <unistd.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *dbenv;
+ int r;
+ int do_private;
+
+ for (do_private=0; do_private<2; do_private++) {
+ if (do_private==0) continue; // See #208.
+ int private_flags = do_private ? (DB_CREATE|DB_PRIVATE) : 0;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = db_env_create(&dbenv, 0);
+ CKERR(r);
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, private_flags|DB_INIT_MPOOL, 0);
+ assert(r==ENOENT);
+ dbenv->close(dbenv,0); // free memory
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create(&dbenv, 0);
+ CKERR(r);
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, private_flags|DB_INIT_MPOOL, 0);
+ // PerconaFT has no trouble opening an environment if the directory exists.
+ CKERR(r);
+ assert(r==0);
+ dbenv->close(dbenv,0); // free memory
+ }
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_env_open_open_close.cc b/storage/tokudb/PerconaFT/src/tests/test_db_env_open_open_close.cc
new file mode 100644
index 00000000000..2204bdf4ecf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_env_open_open_close.cc
@@ -0,0 +1,75 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdio.h>
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char*const* argv) {
+ DB_ENV *dbenv;
+ int r;
+ if (argc == 2 && !strcmp(argv[1], "-v")) verbose = 1;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r = db_env_create(&dbenv, 0);
+ assert(r == 0);
+
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0666);
+ assert(r == 0);
+
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0666);
+ if (verbose) printf("r=%d\n", r);
+ assert(r == EINVAL);
+
+ r = dbenv->close(dbenv, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_env_set_errpfx.cc b/storage/tokudb/PerconaFT/src/tests/test_db_env_set_errpfx.cc
new file mode 100644
index 00000000000..1027bab0706
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_env_set_errpfx.cc
@@ -0,0 +1,74 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdio.h>
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *dbenv;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&dbenv, 0);
+ assert(r == 0);
+
+ dbenv->set_errpfx(dbenv, "houdy partners");
+
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
+ assert(r == 0);
+
+ dbenv->set_errpfx(dbenv, "houdy partners");
+
+ r = dbenv->close(dbenv, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_env_set_lg_dir.cc b/storage/tokudb/PerconaFT/src/tests/test_db_env_set_lg_dir.cc
new file mode 100644
index 00000000000..ac9ce45fc2f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_env_set_lg_dir.cc
@@ -0,0 +1,80 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdio.h>
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *dbenv;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&dbenv, 0);
+ assert(r == 0);
+
+ r = dbenv->set_lg_dir(dbenv, ".");
+ assert(r == 0);
+
+ r = dbenv->set_lg_dir(dbenv, ".");
+ assert(r == 0);
+
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, DB_INIT_TXN|DB_INIT_LOG|DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
+ CKERR(r);
+
+ r = dbenv->set_lg_dir(dbenv, ".");
+ assert(r == EINVAL);
+
+ r = dbenv->close(dbenv, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_env_set_tmp_dir.cc b/storage/tokudb/PerconaFT/src/tests/test_db_env_set_tmp_dir.cc
new file mode 100644
index 00000000000..1cea30327ea
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_env_set_tmp_dir.cc
@@ -0,0 +1,80 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdio.h>
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV *dbenv;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&dbenv, 0);
+ assert(r == 0);
+
+ r = dbenv->set_tmp_dir(dbenv, ".");
+ assert(r == 0);
+
+ r = dbenv->set_tmp_dir(dbenv, ".");
+ assert(r == 0);
+
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
+ CKERR(r);
+
+ r = dbenv->set_tmp_dir(dbenv, ".");
+ assert(r == EINVAL);
+
+ r = dbenv->close(dbenv, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_env_strdup_null.cc b/storage/tokudb/PerconaFT/src/tests/test_db_env_strdup_null.cc
new file mode 100644
index 00000000000..48b90d56a57
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_env_strdup_null.cc
@@ -0,0 +1,67 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Do I return EINVAL when passing in NULL for something that would otherwise be strdup'd? */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <db.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+DB *db;
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->set_data_dir(env, NULL); assert(r==EINVAL);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ env->set_errpfx(env, NULL); assert(1); //Did not crash.
+ r=env->set_tmp_dir(env, NULL); assert(r==EINVAL);
+ r=env->close(env, 0); assert(r==0);
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_get_put_flags.cc b/storage/tokudb/PerconaFT/src/tests/test_db_get_put_flags.cc
new file mode 100644
index 00000000000..4db3741c4f0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_get_put_flags.cc
@@ -0,0 +1,172 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <memory.h>
+#include <db.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+typedef struct {
+ uint32_t db_flags;
+ uint32_t flags;
+ int r_expect;
+ int key;
+ int data;
+} PUT_TEST;
+
+typedef struct {
+ PUT_TEST put;
+ uint32_t flags;
+ int r_expect;
+ int key;
+ int data;
+} GET_TEST;
+
+enum testtype {NONE=0, TGET=1, TPUT=2, SGET=3, SPUT=4, SPGET=5};
+
+typedef struct {
+ enum testtype kind;
+ uint32_t flags;
+ int r_expect;
+ int key;
+ int data;
+} TEST;
+
+static DB *dbp;
+static DB_TXN *const null_txn = 0;
+static DB_ENV *dbenv;
+
+static void
+setup (uint32_t flags) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ /* Open/create primary */
+ r = db_env_create(&dbenv, 0); assert(r == 0);
+ r = dbenv->set_redzone(dbenv, 0); CKERR(r);
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+ r = db_create(&dbp, dbenv, 0); CKERR(r);
+ dbp->set_errfile(dbp,0); // Turn off those annoying errors
+ if (flags) {
+ r = dbp->set_flags(dbp, flags); CKERR(r);
+ }
+ r = dbp->open(dbp, NULL, "primary.db", NULL, DB_BTREE, DB_CREATE, 0600); CKERR(r);
+}
+
+static void
+close_dbs (void) {
+ int r;
+ r = dbp->close(dbp, 0); CKERR(r);
+ r = dbenv->close(dbenv, 0); CKERR(r);
+}
+
+static void
+insert_bad_flags (DB* db, uint32_t flags, int r_expect, int keyint, int dataint) {
+ DBT key;
+ DBT data;
+ int r;
+
+ dbt_init(&key, &keyint, sizeof(keyint));
+ dbt_init(&data,&dataint,sizeof(dataint));
+ r = db->put(db, null_txn, &key, &data, flags);
+ CKERR2(r, r_expect);
+}
+
+static void
+get_bad_flags (DB* db, uint32_t flags, int r_expect, int keyint, int dataint) {
+ DBT key;
+ DBT data;
+ int r;
+
+ dbt_init(&key, &keyint, sizeof(keyint));
+ dbt_init(&data,&dataint,sizeof(dataint));
+ r = db->get(db, null_txn, &key, &data, flags);
+ CKERR2(r, r_expect);
+ //Verify things don't change.
+ assert(*(int*)key.data == keyint);
+ assert(*(int*)data.data == dataint);
+}
+
+PUT_TEST put_tests[] = {
+ {0, DB_NODUPDATA, EINVAL, 0, 0}, //r_expect must change to 0, once implemented.
+ {0, 0, 0, 0, 0},
+ {0, DB_NOOVERWRITE, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+};
+const int num_put = sizeof(put_tests) / sizeof(put_tests[0]);
+
+GET_TEST get_tests[] = {
+ {{0, 0, 0, 0, 0}, 0 , 0, 0, 0},
+ {{0, 0, 0, 0, 0}, 0 , 0, 0, 0},
+ {{0, 0, 0, 0, 0}, 0 , 0, 0, 0},
+ {{0, 0, 0, 0, 0}, 0 , 0, 0, 0},
+ {{0, 0, 0, 0, 0}, DB_RMW, EINVAL, 0, 0},
+ {{0, 0, 0, 0, 0}, DB_RMW, EINVAL, 0, 0},
+};
+const int num_get = sizeof(get_tests) / sizeof(get_tests[0]);
+
+int
+test_main(int argc, char *const argv[]) {
+ int i;
+
+ parse_args(argc, argv);
+
+ for (i = 0; i < num_put; i++) {
+ if (verbose) printf("PutTest [%d]\n", i);
+ setup(put_tests[i].db_flags);
+ insert_bad_flags(dbp, put_tests[i].flags, put_tests[i].r_expect, put_tests[i].key, put_tests[i].data);
+ close_dbs();
+ }
+
+ for (i = 0; i < num_get; i++) {
+ if (verbose) printf("GetTest [%d]\n", i);
+ setup(get_tests[i].put.db_flags);
+ insert_bad_flags(dbp, get_tests[i].put.flags, get_tests[i].put.r_expect, get_tests[i].put.key, get_tests[i].put.data);
+ get_bad_flags(dbp, get_tests[i].flags, get_tests[i].r_expect, get_tests[i].key, get_tests[i].data);
+ close_dbs();
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_named_delete_last.cc b/storage/tokudb/PerconaFT/src/tests/test_db_named_delete_last.cc
new file mode 100644
index 00000000000..3bcafc2e994
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_named_delete_last.cc
@@ -0,0 +1,133 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <toku_portability.h>
+#include <memory.h>
+#include <toku_portability.h>
+#include <db.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "test.h"
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+#define FNAME "foo.tokudb"
+const char *name = NULL;
+
+#define NUM 8
+#define MAX_LENGTH (1<<16)
+
+DB_ENV *env;
+
+DB *db;
+DB_TXN *null_txn;
+
+static void
+open_db(void) {
+ int r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, null_txn, FNAME, name, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+}
+
+static void
+delete_db(void) {
+ int r = env->dbremove(env, NULL, FNAME, name, 0); CKERR(r);
+}
+
+static void
+close_db(void) {
+ int r;
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void
+setup_data(void) {
+ int r = db_env_create(&env, 0); CKERR(r);
+ const int envflags = DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK |DB_THREAD |DB_PRIVATE;
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+}
+
+static void
+runtest(void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ setup_data();
+
+ name = "foo";
+ open_db();
+ close_db();
+ delete_db();
+
+ name = "foo1";
+ open_db();
+ close_db();
+ name = "foo2";
+ open_db();
+ close_db();
+ name = "foo1";
+ delete_db();
+ name = "foo2";
+ delete_db();
+
+ name = "foo1";
+ open_db();
+ close_db();
+ name = "foo2";
+ open_db();
+ close_db();
+ name = "foo2";
+ delete_db();
+ name = "foo1";
+ delete_db();
+
+ env->close(env, 0);
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ runtest();
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_no_env.cc b/storage/tokudb/PerconaFT/src/tests/test_db_no_env.cc
new file mode 100644
index 00000000000..da5f57f4a34
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_no_env.cc
@@ -0,0 +1,62 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+#include <signal.h>
+
+static __attribute__((__noreturn__)) void catch_abort (int sig __attribute__((__unused__))) {
+ exit(1);
+}
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ signal (SIGABRT, catch_abort);
+ DB *db;
+ int r;
+ r = db_create(&db, 0, 0);
+ assert(r == 0);
+ r = db->close(db, 0);
+ assert(r == 0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_open_notexist_reopen.cc b/storage/tokudb/PerconaFT/src/tests/test_db_open_notexist_reopen.cc
new file mode 100644
index 00000000000..83762ab421d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_open_notexist_reopen.cc
@@ -0,0 +1,67 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Simple test of logging. Can I start PerconaFT with logging enabled? */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <db.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+DB *db;
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); CKERR(r);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_PRIVATE|DB_INIT_MPOOL|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, NULL, "doesnotexist.db", "testdb", DB_BTREE, 0, 0666); assert(r==ENOENT);
+ r=db->open(db, NULL, "doesnotexist.db", "testdb", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_remove.cc b/storage/tokudb/PerconaFT/src/tests/test_db_remove.cc
new file mode 100644
index 00000000000..9ad7606b058
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_remove.cc
@@ -0,0 +1,79 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <fcntl.h>
+
+DB_TXN * const null_txn = 0;
+
+const char * const fname = "test_db_remove.ft_handle";
+
+static void test_db_remove (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ // create the DB
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db1;
+ r = db_create(&db1, env, 0); assert(r == 0);
+ r = db1->open(db1, null_txn, fname, 0, DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+ r = db1->close(db1, 0); assert(r == 0); //Header has been written to disk
+
+ r = db_create(&db1, env, 0); assert(r == 0);
+ r = db1->open(db1, null_txn, fname, 0, DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ // Now remove it, while it is open.
+ r = env->dbremove(env, NULL, fname, 0, 0);
+ assert(r!=0);
+
+ r = db1->close(db1, 0); assert(r==0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ test_db_remove();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_remove_subdb.cc b/storage/tokudb/PerconaFT/src/tests/test_db_remove_subdb.cc
new file mode 100644
index 00000000000..c0a97d49f7f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_remove_subdb.cc
@@ -0,0 +1,125 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Does removing subdatabases corrupt the db file/other dbs in that file? (when nothing else open) */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <db.h>
+#include <memory.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+DB *db;
+DBT key;
+DBT data;
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ dbt_init(&key, "name", sizeof "name");
+ dbt_init(&data, NULL, 0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ // Note: without DB_INIT_MPOOL the BDB library will fail on db->open().
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=env->dbremove(env, NULL, "DoesNotExist.db", NULL, 0); assert(r==ENOENT);
+
+ r=env->dbremove(env, NULL, "DoesNotExist.db", "SubDb", 0); assert(r==ENOENT);
+
+ r=db_create(&db, env, 0); assert(r==0);
+ r=db->open(db, NULL, "master.db", "first", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ dbt_init(&data, "first.db", sizeof "first.db");
+ db->put(db, NULL, &key, &data, 0);
+ r=db->close(db, 0); assert(r==0);
+
+ r=env->dbremove(env, NULL, "master.db", "second", 0); assert(r==ENOENT);
+
+ r=db_create(&db, env, 0); assert(r==0);
+ r=db->open(db, NULL, "master.db", "second", DB_BTREE, DB_CREATE, 0666); assert(r==0);
+ dbt_init(&key, "name", sizeof "name");
+ dbt_init(&data, "second.db", sizeof "second.db");
+ db->put(db, NULL, &key, &data, 0);
+ r=db->close(db, 0); assert(r==0);
+
+ r=db_create(&db, env, 0); assert(r==0);
+ r=db->open(db, NULL, "master.db", "third", DB_BTREE, DB_CREATE, 0666); assert(r==0);
+ dbt_init(&key, "name", sizeof "name");
+ dbt_init(&data, "third.db", sizeof "third.db");
+ db->put(db, NULL, &key, &data, 0);
+ r=db->close(db, 0); assert(r==0);
+
+ r=env->dbremove(env, NULL, "master.db", "second", 0); assert(r==0);
+
+ r=env->dbremove(env, NULL, "master.db", "second", 0); assert(r==ENOENT);
+
+ dbt_init(&key, "name", sizeof "name");
+ dbt_init(&data, NULL, 0);
+
+ //Verify data still exists in first/third
+ r=db_create(&db, env, 0); assert(r==0);
+ r=db->open(db, NULL, "master.db", "first", DB_BTREE, 0, 0666); assert(r==0);
+ r=db->get(db, NULL, &key, &data, 0); assert(r==0);
+ assert(!strcmp((char*)data.data, "first.db"));
+ r=db->close(db, 0); assert(r==0);
+
+ r=db_create(&db, env, 0); assert(r==0);
+ r=db->open(db, NULL, "master.db", "third", DB_BTREE, 0, 0666); assert(r==0);
+ r=db->get(db, NULL, &key, &data, 0); assert(r==0);
+ assert(!strcmp((char*)data.data, "third.db"));
+ r=db->close(db, 0); assert(r==0);
+
+ //Verify second is gone.
+ r=db_create(&db, env, 0); assert(r==0);
+ r=db->open(db, NULL, "master.db", "second", DB_BTREE, 0, 0666); assert(r==ENOENT);
+ //Create again, verify it does not have its old data.
+ r=db->open(db, NULL, "master.db", "second", DB_BTREE, DB_CREATE, 0666); assert(r==0);
+ r=db->get(db, NULL, &key, &data, 0); assert(r==DB_NOTFOUND);
+
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_set_flags.cc b/storage/tokudb/PerconaFT/src/tests/test_db_set_flags.cc
new file mode 100644
index 00000000000..ac381c467cb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_set_flags.cc
@@ -0,0 +1,84 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_db_set_flags (int flags, int expectr, int flags2, int expectr2) {
+ if (verbose) printf("test_db_set_flags:%d %d %d %d\n", flags, expectr, flags2, expectr2);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.db.set.flags.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->set_flags(db, flags); assert(r == expectr);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+ r = db->set_flags(db, flags2); assert(r == expectr2);
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ test_db_set_flags(0, 0, 0, 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_subdb.cc b/storage/tokudb/PerconaFT/src/tests/test_db_subdb.cc
new file mode 100644
index 00000000000..0d8468ef89e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_subdb.cc
@@ -0,0 +1,95 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <db.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV * env = 0;
+ DB *db;
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.db";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ // Note: without DB_INIT_MPOOL the BDB library will fail on db->open().
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_PRIVATE|DB_CREATE|DB_INIT_LOG|DB_INIT_TXN, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r = db_create(&db, env, 0);
+ CKERR(r);
+
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+#if 0
+ const char * const fname2 = "test2.db";
+ // This sequence segfaults in BDB 4.3.29
+ // See what happens if we open a database with a subdb, when the file has only the main db.
+ r = db->open(db, null_txn, fname2, 0, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ r = db->close(db,0);
+ CKERR(r);
+ r = db->open(db, null_txn, fname2, "main", DB_BTREE, 0, 0666);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+#endif
+
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_subdb_different_flags.cc b/storage/tokudb/PerconaFT/src/tests/test_db_subdb_different_flags.cc
new file mode 100644
index 00000000000..46e1f89f911
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_subdb_different_flags.cc
@@ -0,0 +1,114 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <db.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ DB_ENV * env = 0;
+ DB *db;
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.db";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ // Note: without DB_INIT_MPOOL the BDB library will fail on db->open().
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_PRIVATE|DB_CREATE|DB_INIT_LOG|DB_INIT_TXN, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, fname, "subdb", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, fname, "subdb2", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ uint32_t flags;
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, 0, 0666); CKERR(r);
+ r = db->get_flags(db, &flags); CKERR(r); assert(flags==0);
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, fname, "subdb", DB_BTREE, 0, 0666); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, fname, "subdb2", DB_BTREE, 0, 0666); CKERR(r);
+ r = db->get_flags(db, &flags); CKERR(r); assert(flags==0);
+ r = db->close(db, 0); CKERR(r);
+
+#if 0
+ const char * const fname2 = "test2.db";
+ // This sequence segfaults in BDB 4.3.29
+ // See what happens if we open a database with a subdb, when the file has only the main db.
+ r = db->open(db, null_txn, fname2, 0, DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+ r = db->close(db,0);
+ CKERR(r);
+ r = db->open(db, null_txn, fname2, "main", DB_BTREE, 0, 0666);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+#endif
+
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_txn_locks_nonheaviside.cc b/storage/tokudb/PerconaFT/src/tests/test_db_txn_locks_nonheaviside.cc
new file mode 100644
index 00000000000..fc01992eea7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_txn_locks_nonheaviside.cc
@@ -0,0 +1,612 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <toku_portability.h>
+#include <memory.h>
+#include <toku_portability.h>
+#include <db.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "test.h"
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+static DB *db;
+static DB_TXN* txns[(int)256];
+static DB_ENV* dbenv;
+static DBC* cursors[(int)256];
+
+static void
+put(bool success, char txn, int _key, int _data) {
+ assert(txns[(int)txn]);
+
+ int r;
+ DBT key;
+ DBT data;
+
+ r = db->put(db, txns[(int)txn],
+ dbt_init(&key, &_key, sizeof(int)),
+ dbt_init(&data, &_data, sizeof(int)),
+ 0);
+
+ if (success) CKERR(r);
+ else CKERR2s(r, DB_LOCK_DEADLOCK, DB_LOCK_NOTGRANTED);
+}
+
+static void
+cget(bool success, bool find, char txn, int _key, int _data,
+ int _key_expect, int _data_expect, uint32_t flags) {
+ assert(txns[(int)txn] && cursors[(int)txn]);
+
+ int r;
+ DBT key;
+ DBT data;
+
+ r = cursors[(int)txn]->c_get(cursors[(int)txn],
+ dbt_init(&key, &_key, sizeof(int)),
+ dbt_init(&data, &_data, sizeof(int)),
+ flags);
+ if (success) {
+ if (find) {
+ CKERR(r);
+ assert(*(int *)key.data == _key_expect);
+ assert(*(int *)data.data == _data_expect);
+ }
+ else CKERR2(r, DB_NOTFOUND);
+ }
+ else CKERR2s(r, DB_LOCK_DEADLOCK, DB_LOCK_NOTGRANTED);
+}
+
+static void
+dbdel (bool success, bool find, char txn, int _key) {
+ int r;
+ DBT key;
+
+ /* If DB_DELETE_ANY changes to 0, then find is meaningful and
+ has to be fixed in test_dbdel*/
+ r = db->del(db, txns[(int)txn], dbt_init(&key,&_key, sizeof(int)),
+ DB_DELETE_ANY);
+ if (success) {
+ if (find) CKERR(r);
+ else CKERR2( r, DB_NOTFOUND);
+ }
+ else CKERR2s(r, DB_LOCK_DEADLOCK, DB_LOCK_NOTGRANTED);
+}
+
+static void
+init_txn (char name) {
+ int r;
+ assert(!txns[(int)name]);
+ r = dbenv->txn_begin(dbenv, NULL, &txns[(int)name], DB_TXN_NOWAIT);
+ CKERR(r);
+ assert(txns[(int)name]);
+}
+
+static void
+init_dbc (char name) {
+ int r;
+
+ assert(!cursors[(int)name] && txns[(int)name]);
+ r = db->cursor(db, txns[(int)name], &cursors[(int)name], 0);
+ CKERR(r);
+ assert(cursors[(int)name]);
+}
+
+static void
+commit_txn (char name) {
+ int r;
+ assert(txns[(int)name] && !cursors[(int)name]);
+
+ r = txns[(int)name]->commit(txns[(int)name], 0);
+ CKERR(r);
+ txns[(int)name] = NULL;
+}
+
+static void
+abort_txn (char name) {
+ int r;
+ assert(txns[(int)name] && !cursors[(int)name]);
+
+ r = txns[(int)name]->abort(txns[(int)name]);
+ CKERR(r);
+ txns[(int)name] = NULL;
+}
+
+static void
+close_dbc (char name) {
+ int r;
+
+ assert(cursors[(int)name]);
+ r = cursors[(int)name]->c_close(cursors[(int)name]);
+ CKERR(r);
+ cursors[(int)name] = NULL;
+}
+
+static void
+early_commit (char name) {
+ assert(cursors[(int)name] && txns[(int)name]);
+ close_dbc(name);
+ commit_txn(name);
+}
+
+static void
+early_abort (char name) {
+ assert(cursors[(int)name] && txns[(int)name]);
+ close_dbc(name);
+ abort_txn(name);
+}
+
+static void
+setup_dbs (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ dbenv = NULL;
+ db = NULL;
+ /* Open/create primary */
+ r = db_env_create(&dbenv, 0);
+ CKERR(r);
+ r = dbenv->set_default_bt_compare(dbenv, int_dbt_cmp);
+ CKERR(r);
+ uint32_t env_txn_flags = DB_INIT_TXN | DB_INIT_LOCK;
+ uint32_t env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL;
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, env_open_flags | env_txn_flags, 0600);
+ CKERR(r);
+
+ r = db_create(&db, dbenv, 0);
+ CKERR(r);
+
+ char a;
+ for (a = 'a'; a <= 'z'; a++) init_txn(a);
+ init_txn('\0');
+ r = db->open(db, txns[(int)'\0'], "foobar.db", NULL, DB_BTREE, DB_CREATE, 0600);
+ CKERR(r);
+ commit_txn('\0');
+ for (a = 'a'; a <= 'z'; a++) init_dbc(a);
+}
+
+static void
+close_dbs(void) {
+ char a;
+ for (a = 'a'; a <= 'z'; a++) {
+ if (cursors[(int)a]) close_dbc(a);
+ if (txns[(int)a]) commit_txn(a);
+ }
+
+ int r;
+ r = db->close(db, 0);
+ CKERR(r);
+ db = NULL;
+ r = dbenv->close(dbenv, 0);
+ CKERR(r);
+ dbenv = NULL;
+}
+
+
+static __attribute__((__unused__))
+void
+test_abort (void) {
+ /* ********************************************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ early_abort('a');
+ cget(true, false, 'b', 1, 1, 0, 0, DB_SET);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 1, 1, 0, 0, DB_SET);
+ cget(true, false, 'b', 1, 1, 0, 0, DB_SET);
+ put(false, 'a', 1, 1);
+ early_commit('b');
+ put(true, 'a', 1, 1);
+ cget(true, true, 'a', 1, 1, 1, 1, DB_SET);
+ cget(true, false, 'a', 2, 1, 1, 1, DB_SET);
+ cget(false, true, 'c', 1, 1, 0, 0, DB_SET);
+ early_abort('a');
+ cget(true, false, 'c', 1, 1, 0, 0, DB_SET);
+ close_dbs();
+ /* ********************************************************************** */
+}
+
+static void
+test_both (uint32_t db_flags) {
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 1, 1, 0, 0, db_flags);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 1, 1, 0, 0, db_flags);
+ cget(true, false, 'a', 2, 1, 0, 0, db_flags);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 1, 1, 0, 0, db_flags);
+ cget(true, false, 'a', 1, 1, 0, 0, db_flags);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 1, 1, 0, 0, db_flags);
+ cget(true, false, 'b', 2, 1, 0, 0, db_flags);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 1, 1, 0, 0, db_flags);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ cget(false, false, 'b', 1, 1, 0, 0, db_flags);
+#else
+ cget(true, false, 'b', 1, 1, 0, 0, db_flags);
+#endif
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 1, 1, 0, 0, db_flags);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ cget(false, false, 'b', 1, 1, 0, 0, db_flags);
+ put(true, 'a', 1, 1);
+#else
+ cget(true, false, 'b', 1, 1, 0, 0, db_flags);
+ put(false, 'a', 1, 1);
+#endif
+ early_commit('b');
+ put(true, 'a', 1, 1);
+ cget(true, true, 'a', 1, 1, 1, 1, db_flags);
+ cget(true, false, 'a', 2, 1, 0, 0, db_flags);
+ cget(false, true, 'c', 1, 1, 0, 0, db_flags);
+ early_commit('a');
+ cget(true, true, 'c', 1, 1, 1, 1, db_flags);
+ close_dbs();
+}
+
+
+static void
+test_last (void) {
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 0, 0, 0, 0, DB_LAST);
+ put(false, 'b', 2, 1);
+ put(true, 'a', 2, 1);
+ cget(true, true, 'a', 0, 0, 2, 1, DB_LAST);
+ early_commit('a');
+ put(true, 'b', 2, 1);
+ close_dbs();
+ /* ****************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ cget(true, true, 'a', 0, 0, 1, 1, DB_LAST);
+ put(false, 'b', 2, 1);
+ put(true, 'b', -1, 1);
+ cget(true, true, 'a', 0, 0, 1, 1, DB_LAST);
+ close_dbs();
+ /* ****************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ put(true, 'a', 3, 1);
+ put(true, 'a', 6, 1);
+ cget(true, true, 'a', 0, 0, 6, 1, DB_LAST);
+ put(true, 'b', 2, 1);
+ put(true, 'b', 4, 1);
+ put(false, 'b', 7, 1);
+ put(true, 'b', -1, 1);
+ close_dbs();
+ /* ****************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ cget(true, true, 'a', 0, 0, 1, 1, DB_LAST);
+ put(false, 'b', 1, 0);
+ close_dbs();
+}
+
+static void
+test_first (void) {
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', 0, 0, 0, 0, DB_FIRST);
+ put(false, 'b', 2, 1);
+ put(true, 'a', 2, 1);
+ cget(true, true, 'a', 0, 0, 2, 1, DB_FIRST);
+ early_commit('a');
+ put(true, 'b', 2, 1);
+ close_dbs();
+ /* ****************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ cget(true, true, 'a', 0, 0, 1, 1, DB_FIRST);
+ put(true, 'b', 2, 1);
+ put(false, 'b', -1, 1);
+ cget(true, true, 'a', 0, 0, 1, 1, DB_FIRST);
+ close_dbs();
+ /* ****************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ put(true, 'a', 3, 1);
+ put(true, 'a', 6, 1);
+ cget(true, true, 'a', 0, 0, 1, 1, DB_FIRST);
+ put(true, 'b', 2, 1);
+ put(true, 'b', 4, 1);
+ put(true, 'b', 7, 1);
+ put(false, 'b', -1, 1);
+ close_dbs();
+ /* ****************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ cget(true, true, 'a', 0, 0, 1, 1, DB_FIRST);
+ put(false, 'b', 1, 2);
+ close_dbs();
+}
+
+static void
+test_set_range (uint32_t flag, int i) {
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', i*1, i*1, 0, 0, flag);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', i*1, i*1, 0, 0, flag);
+ cget(true, false, 'a', i*2, i*1, 0, 0, flag);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', i*1, i*1, 0, 0, flag);
+ cget(true, false, 'a', i*1, i*1, 0, 0, flag);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', i*1, i*1, 0, 0, flag);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ cget(false, false, 'b', i*2, i*1, 0, 0, flag);
+#else
+ cget(true, false, 'b', i*2, i*1, 0, 0, flag);
+#endif
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', i*1, i*1, 0, 0, flag);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ cget(false, false, 'b', i*1, i*1, 0, 0, flag);
+#else
+ cget(true, false, 'b', i*1, i*1, 0, 0, flag);
+#endif
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ cget(true, false, 'a', i*1, i*1, 0, 0, flag);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ cget(false, false, 'b', i*5, i*5, 0, 0, flag);
+ put(true, 'a', i*7, i*6);
+ put(true, 'a', i*5, i*5);
+#else
+ cget(true, false, 'b', i*5, i*5, 0, 0, flag);
+ put(false, 'a', i*7, i*6);
+ put(false, 'a', i*5, i*5);
+#endif
+ put(true, 'a', i*4, i*4);
+ put(true, 'b', -i*1, i*4);
+ put(false, 'b', i*2, i*4);
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ put(true, 'a', i*5, i*4);
+#else
+ put(false, 'a', i*5, i*4);
+#endif
+ early_commit('b');
+ put(true, 'a', i*7, i*6);
+ put(true, 'a', i*5, i*5);
+ put(true, 'a', i*4, i*4);
+ put(true, 'a', i*5, i*4);
+ cget(true, true, 'a', i*1, i*1, i*4, i*4, flag);
+ cget(true, true, 'a', i*2, i*1, i*4, i*4, flag);
+ cget(false, true, 'c', i*6, i*6, i*7, i*6, flag);
+ early_commit('a');
+ cget(true, true, 'c', i*6, i*6, i*7, i*6, flag);
+ close_dbs();
+}
+
+static void
+test_next (uint32_t next_type) {
+ /* ********************************************************************** */
+ setup_dbs();
+ put(true, 'a', 2, 1);
+ put(true, 'a', 5, 1);
+ cget(true, true, 'a', 0, 0, 2, 1, next_type);
+ put(false, 'b', 2, 1);
+ put(true, 'b', 4, 1);
+ put(false, 'b', -1, 1);
+ cget(false, true, 'a', 0, 0, 4, 1, next_type);
+ early_commit('b');
+ cget(true, true, 'a', 2, 1, 2, 1, DB_SET);
+ cget(true, true, 'a', 0, 0, 4, 1, next_type);
+ cget(true, true, 'a', 0, 0, 5, 1, next_type);
+ close_dbs();
+ /* ****************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ put(true, 'a', 3, 1);
+ put(true, 'a', 6, 1);
+ cget(true, true, 'a', 0, 0, 1, 1, next_type);
+ cget(true, true, 'a', 0, 0, 3, 1, next_type);
+ put(false, 'b', 2, 1);
+ put(true, 'b', 4, 1);
+ put(true, 'b', 7, 1);
+ put(false, 'b', -1, 1);
+ close_dbs();
+}
+
+static void
+test_prev (uint32_t next_type) {
+ /* ********************************************************************** */
+ setup_dbs();
+ put(true, 'a', -2, -1);
+ put(true, 'a', -5, -1);
+ cget(true, true, 'a', 0, 0, -2, -1, next_type);
+ put(false, 'b', -2, -1);
+ put(true, 'b', -4, -1);
+ put(false, 'b', 1, -1);
+ cget(false, true, 'a', 0, 0, -4, -1, next_type);
+ early_commit('b');
+ cget(true, true, 'a', -2, -1, -2, -1, DB_SET);
+ cget(true, true, 'a', 0, 0, -4, -1, next_type);
+ cget(true, true, 'a', 0, 0, -5, -1, next_type);
+ close_dbs();
+ /* ****************************************** */
+ setup_dbs();
+ put(true, 'a', -1, -1);
+ put(true, 'a', -3, -1);
+ put(true, 'a', -6, -1);
+ cget(true, true, 'a', 0, 0, -1, -1, next_type);
+ cget(true, true, 'a', 0, 0, -3, -1, next_type);
+ put(false, 'b', -2, -1);
+ put(true, 'b', -4, -1);
+ put(true, 'b', -7, -1);
+ put(false, 'b', 1, -1);
+ close_dbs();
+}
+
+static void
+test_dbdel (void) {
+ /* If DB_DELETE_ANY changes to 0, then find is meaningful and
+ has to be fixed in test_dbdel*/
+ /* ********************************************************************** */
+ setup_dbs();
+ put(true, 'c', 1, 1);
+ early_commit('c');
+ dbdel(true, true, 'a', 1);
+ cget(false, true, 'b', 1, 1, 1, 1, DB_SET);
+ cget(false, true, 'b', 1, 4, 1, 4, DB_SET);
+ cget(false, true, 'b', 1, 0, 1, 4, DB_SET);
+ cget(true, false, 'b', 0, 0, 0, 0, DB_SET);
+ cget(true, false, 'b', 2, 10, 2, 10, DB_SET);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ dbdel(true, true, 'a', 1);
+ cget(false, true, 'b', 1, 1, 1, 1, DB_SET);
+ cget(false, true, 'b', 1, 4, 1, 4, DB_SET);
+ cget(false, true, 'b', 1, 0, 1, 4, DB_SET);
+ cget(true, false, 'b', 0, 0, 0, 0, DB_SET);
+ cget(true, false, 'b', 2, 10, 2, 10, DB_SET);
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ put(true, 'c', 1, 1);
+ early_commit('c');
+ cget(true, true, 'b', 1, 1, 1, 1, DB_SET);
+ dbdel(false, true, 'a', 1);
+ dbdel(true, true, 'a', 2);
+ dbdel(true, true, 'a', 0);
+ close_dbs();
+}
+
+static void
+test_current (void) {
+ /* ********************************************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ early_commit('a');
+ cget(true, true, 'b', 1, 1, 1, 1, DB_SET);
+ cget(true, true, 'b', 1, 1, 1, 1, DB_CURRENT);
+ close_dbs();
+}
+
+struct dbt_pair {
+ DBT key;
+ DBT val;
+};
+
+struct int_pair {
+ int key;
+ int val;
+};
+
+int got_r_h;
+
+static __attribute__((__unused__))
+void
+ignore (void *ignore __attribute__((__unused__))) {
+}
+#define TOKU_IGNORE(x) ignore((void*)x)
+
+static void
+test (void) {
+ /* ********************************************************************** */
+ setup_dbs();
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ early_abort('a');
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ early_commit('a');
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ put(true, 'a', 1, 1);
+ close_dbs();
+ /* ********************************************************************** */
+ test_both( DB_SET);
+ /* ********************************************************************** */
+ test_first();
+ /* ********************************************************************** */
+ test_last();
+ /* ********************************************************************** */
+ test_set_range( DB_SET_RANGE, 1);
+#ifdef DB_SET_RANGE_REVERSE
+ test_set_range( DB_SET_RANGE_REVERSE, -1);
+#endif
+ /* ********************************************************************** */
+ test_next(DB_NEXT);
+ /* ********************************************************************** */
+ test_prev(DB_PREV);
+ /* ********************************************************************** */
+ test_dbdel();
+ /* ********************************************************************** */
+ test_current();
+ /* ********************************************************************** */
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_txn_locks_read_uncommitted.cc b/storage/tokudb/PerconaFT/src/tests/test_db_txn_locks_read_uncommitted.cc
new file mode 100644
index 00000000000..c42eba2889a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_txn_locks_read_uncommitted.cc
@@ -0,0 +1,241 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <memory.h>
+#include <db.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+static DB *db;
+static DB_TXN* txns[(int)256];
+static DB_ENV* dbenv;
+static DBC* cursors[(int)256];
+
+static void
+put(bool success, char txn, int _key, int _data) {
+ assert(txns[(int)txn]);
+
+ int r;
+ DBT key;
+ DBT data;
+
+ r = db->put(db, txns[(int)txn],
+ dbt_init(&key, &_key, sizeof(int)),
+ dbt_init(&data, &_data, sizeof(int)),
+ 0);
+
+ if (success) CKERR(r);
+ else CKERR2s(r, DB_LOCK_DEADLOCK, DB_LOCK_NOTGRANTED);
+}
+
+static void
+init_txn (char name, uint32_t flags) {
+ int r;
+ assert(!txns[(int)name]);
+ r = dbenv->txn_begin(dbenv, NULL, &txns[(int)name], DB_TXN_NOWAIT | flags);
+ CKERR(r);
+ assert(txns[(int)name]);
+}
+
+static void
+init_dbc (char name) {
+ int r;
+
+ assert(!cursors[(int)name] && txns[(int)name]);
+ r = db->cursor(db, txns[(int)name], &cursors[(int)name], 0);
+ CKERR(r);
+ assert(cursors[(int)name]);
+}
+
+static void
+commit_txn (char name) {
+ int r;
+ assert(txns[(int)name] && !cursors[(int)name]);
+
+ r = txns[(int)name]->commit(txns[(int)name], 0);
+ CKERR(r);
+ txns[(int)name] = NULL;
+}
+
+
+static void
+close_dbc (char name) {
+ int r;
+
+ assert(cursors[(int)name]);
+ r = cursors[(int)name]->c_close(cursors[(int)name]);
+ CKERR(r);
+ cursors[(int)name] = NULL;
+}
+
+static void
+early_commit (char name) {
+ assert(cursors[(int)name] && txns[(int)name]);
+ close_dbc(name);
+ commit_txn(name);
+}
+
+static void
+setup_dbs (void) {
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ dbenv = NULL;
+ db = NULL;
+ /* Open/create primary */
+ r = db_env_create(&dbenv, 0);
+ CKERR(r);
+ r = dbenv->set_default_bt_compare(dbenv, int_dbt_cmp);
+ CKERR(r);
+ uint32_t env_txn_flags = DB_INIT_TXN | DB_INIT_LOCK;
+ uint32_t env_open_flags = DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL;
+ r = dbenv->open(dbenv, TOKU_TEST_FILENAME, env_open_flags | env_txn_flags, 0600);
+ CKERR(r);
+
+ r = db_create(&db, dbenv, 0);
+ CKERR(r);
+
+ char a;
+ for (a = 'a'; a <= 'z'; a++) init_txn(a, 0);
+ for (a = '0'; a <= '9'; a++) init_txn(a, DB_READ_UNCOMMITTED);
+ init_txn('\0', 0);
+ r = db->open(db, txns[(int)'\0'], "foobar.db", NULL, DB_BTREE, DB_CREATE | DB_READ_UNCOMMITTED, 0600);
+ CKERR(r);
+ commit_txn('\0');
+ for (a = 'a'; a <= 'z'; a++) init_dbc(a);
+ for (a = '0'; a <= '9'; a++) init_dbc(a);
+}
+
+static void
+close_dbs(void) {
+ char a;
+ for (a = 'a'; a <= 'z'; a++) {
+ if (cursors[(int)a]) close_dbc(a);
+ if (txns[(int)a]) commit_txn(a);
+ }
+ for (a = '0'; a <= '9'; a++) {
+ if (cursors[(int)a]) close_dbc(a);
+ if (txns[(int)a]) commit_txn(a);
+ }
+
+ int r;
+ r = db->close(db, 0);
+ CKERR(r);
+ db = NULL;
+ r = dbenv->close(dbenv, 0);
+ CKERR(r);
+ dbenv = NULL;
+}
+
+
+static void
+table_scan(char txn, bool success) {
+ int r;
+ DBT key;
+ DBT data;
+
+ assert(txns[(int)txn] && cursors[(int)txn]);
+ r = cursors[(int)txn]->c_get(cursors[(int)txn],
+ dbt_init(&key, 0, 0),
+ dbt_init(&data, 0, 0),
+ DB_FIRST);
+ while (r==0) {
+ r = cursors[(int)txn]->c_get(cursors[(int)txn],
+ dbt_init(&key, 0, 0),
+ dbt_init(&data, 0, 0),
+ DB_NEXT);
+ }
+#ifdef BLOCKING_ROW_LOCKS_READS_NOT_SHARED
+ if (success) invariant(r == DB_NOTFOUND || r == DB_LOCK_NOTGRANTED || r == DB_LOCK_DEADLOCK);
+ else CKERR2s(r, DB_LOCK_NOTGRANTED, DB_LOCK_DEADLOCK);
+#else
+ if (success) CKERR2(r, DB_NOTFOUND);
+ else CKERR2s(r, DB_LOCK_NOTGRANTED, DB_LOCK_DEADLOCK);
+#endif
+}
+
+static void
+table_prelock(char txn, bool success) {
+ int r;
+ r = db->pre_acquire_table_lock(db, txns[(int)txn]);
+ if (success) CKERR(r);
+ else CKERR2s(r, DB_LOCK_NOTGRANTED, DB_LOCK_DEADLOCK);
+}
+
+static void
+test (void) {
+ char txn;
+ /* ********************************************************************** */
+ setup_dbs();
+ close_dbs();
+ /* ********************************************************************** */
+ setup_dbs();
+ table_scan('0', true);
+ table_prelock('a', true);
+ put(true, 'a', 0, 0);
+ for (txn = 'b'; txn<'z'; txn++) {
+ table_scan(txn, false);
+ }
+ for (txn = '0'; txn<'9'; txn++) {
+ table_scan(txn, true);
+ }
+ early_commit('a');
+ for (txn = 'b'; txn<'z'; txn++) {
+ table_scan(txn, true);
+ }
+ for (txn = '0'; txn<'9'; txn++) {
+ table_scan(txn, true);
+ }
+ close_dbs();
+ /* ********************************************************************** */
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_db_version.cc b/storage/tokudb/PerconaFT/src/tests/test_db_version.cc
new file mode 100644
index 00000000000..78909a14bb0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_db_version.cc
@@ -0,0 +1,61 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <db.h>
+
+
+
+int
+test_main (int argc, char *const argv[]) {
+ const char *v;
+ int major, minor, patch;
+ parse_args(argc, argv);
+ v = db_version(0, 0, 0);
+ assert(v!=0);
+ v = db_version(&major, &minor, &patch);
+ assert(major==DB_VERSION_MAJOR);
+ assert(minor==DB_VERSION_MINOR);
+ assert(patch==DB_VERSION_PATCH);
+ if (verbose) {
+ printf("%d.%d.%d\n", major, minor, patch);
+ printf("%s\n", v);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_env_close_flags.cc b/storage/tokudb/PerconaFT/src/tests/test_env_close_flags.cc
new file mode 100644
index 00000000000..def7f7bd8cf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_env_close_flags.cc
@@ -0,0 +1,84 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+
+#include <db.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ DB_ENV *env;
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env,0); // Turn off those annoying errors
+ r=env->close (env, 0); assert(r==0);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env,0); // Turn off those annoying errors
+ r=env->close (env, 1);
+ assert(r==EINVAL);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env,0); // Turn off those annoying errors
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=env->close (env, 0); assert(r==0);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env,0); // Turn off those annoying errors
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=env->close (env, 1);
+ assert(r==EINVAL);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_env_create_db_create.cc b/storage/tokudb/PerconaFT/src/tests/test_env_create_db_create.cc
new file mode 100644
index 00000000000..249055ca0b3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_env_create_db_create.cc
@@ -0,0 +1,60 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ DB_ENV *env;
+ DB *db;
+ int r;
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+ r = db_create(&db, env, 0);
+ assert(r != 0);
+ r = env->close(env, 0);
+ assert(r == 0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_env_open_flags.cc b/storage/tokudb/PerconaFT/src/tests/test_env_open_flags.cc
new file mode 100644
index 00000000000..715bc5524f3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_env_open_flags.cc
@@ -0,0 +1,92 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_env_open_flags (int env_open_flags, int expectr) {
+ if (verbose) printf("test_env_open_flags:%d\n", env_open_flags);
+
+ DB_ENV *env;
+ int r;
+
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+ env->set_errfile(env, 0);
+
+ r = env->open(env, TOKU_TEST_FILENAME, env_open_flags, 0644);
+ if (r != expectr && verbose) printf("env open flags=%x expectr=%d r=%d\n", env_open_flags, expectr, r);
+
+ r = env->close(env, 0);
+ assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ char tracefile[TOKU_PATH_MAX+1];
+ toku_set_trace_file(toku_path_join(tracefile, 2, TOKU_TEST_FILENAME, "trace.tktrace"));
+
+ /* test flags */
+ test_env_open_flags(0, ENOENT);
+ // This one segfaults in BDB 4.6.21
+ test_env_open_flags(DB_PRIVATE, ENOENT);
+ test_env_open_flags(DB_PRIVATE+DB_CREATE, 0);
+ test_env_open_flags(DB_PRIVATE+DB_CREATE+DB_INIT_MPOOL, 0);
+ test_env_open_flags(DB_PRIVATE+DB_RECOVER, EINVAL);
+ test_env_open_flags(DB_PRIVATE+DB_CREATE+DB_INIT_MPOOL+DB_RECOVER, EINVAL);
+
+ toku_close_trace_file();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_equal_keys_with_different_bytes.cc b/storage/tokudb/PerconaFT/src/tests/test_equal_keys_with_different_bytes.cc
new file mode 100644
index 00000000000..7a027c4c222
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_equal_keys_with_different_bytes.cc
@@ -0,0 +1,97 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <string>
+
+#include "test.h"
+
+static int compare_strings_case_insensitive(DB *db, const DBT *a, const DBT *b) {
+ invariant_notnull(db);
+ return strcasecmp(reinterpret_cast<char *>(a->data),
+ reinterpret_cast<char *>(b->data));
+}
+
+static void test_equal_keys_with_different_bytes(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, compare_strings_case_insensitive);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL+DB_INIT_TXN, 0); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ DBT key;
+
+ // put 'key'
+ dbt_init(&key, "key", sizeof("key"));
+ r = db->put(db, NULL, &key, &key, 0); CKERR(r);
+
+ // del 'KEY' - should match 'key'
+ dbt_init(&key, "KEY", sizeof("KEY"));
+ r = db->del(db, NULL, &key, 0); CKERR(r);
+
+ DBT val;
+ char val_buf[10];
+ dbt_init(&val, val_buf, sizeof(val_buf));
+
+ // search should fail for 'key'
+ dbt_init(&key, "key", sizeof("key"));
+ r = db->get(db, NULL, &key, &val, 0); CKERR2(r, DB_NOTFOUND);
+
+ // search should fail for 'KEY'
+ dbt_init(&key, "KEY", sizeof("KEY"));
+ r = db->get(db, NULL, &key, &val, 0); CKERR2(r, DB_NOTFOUND);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ test_equal_keys_with_different_bytes();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_error.cc b/storage/tokudb/PerconaFT/src/tests/test_error.cc
new file mode 100644
index 00000000000..8f939a15d90
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_error.cc
@@ -0,0 +1,131 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+char const* expect_errpfx;
+int n_handle_error=0;
+
+static void
+handle_error (const DB_ENV *UU(dbenv), const char *errpfx, const char *UU(msg)) {
+ assert(errpfx==expect_errpfx);
+ n_handle_error++;
+}
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ {
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env,0); // Turn off those annoying errors
+ r = env->open(env, TOKU_TEST_FILENAME, (uint32_t) -1, 0644);
+ CKERR2(r, EINVAL);
+ assert(n_handle_error==0);
+ r = env->close(env, 0); assert(r==0);
+ }
+
+ int do_errfile, do_errcall,do_errpfx;
+ for (do_errpfx=0; do_errpfx<2; do_errpfx++) {
+ for (do_errfile=0; do_errfile<2; do_errfile++) {
+ for (do_errcall=0; do_errcall<2; do_errcall++) {
+ char errfname[TOKU_PATH_MAX+1];
+ toku_path_join(errfname, 2, TOKU_TEST_FILENAME, "errfile");
+ unlink(errfname);
+ {
+ DB_ENV *env;
+ FILE *write_here = fopen(errfname, "w");
+ assert(write_here);
+ n_handle_error=0;
+ r = db_env_create(&env, 0); assert(r==0);
+ if (do_errpfx) {
+ expect_errpfx="whoopi";
+ env->set_errpfx(env, expect_errpfx);
+ } else {
+ expect_errpfx=0;
+ }
+ env->set_errfile(env,0); // Turn off those annoying errors
+ if (do_errfile)
+ env->set_errfile(env, write_here);
+ if (do_errcall)
+ env->set_errcall(env, handle_error);
+ r = env->open(env, TOKU_TEST_FILENAME, (uint32_t) -1, 0644);
+ assert(r==EINVAL);
+ r = env->close(env, 0); assert(r==0);
+ fclose(write_here);
+ }
+ {
+ FILE *read_here = fopen(errfname, "r");
+ assert(read_here);
+ char buf[10000];
+ int buflen = fread(buf, 1, sizeof(buf)-1, read_here);
+ assert(buflen>=0);
+ buf[buflen]=0;
+ if (do_errfile) {
+ if (do_errpfx) {
+ assert(strncmp(buf,"whoopi:",6)==0);
+ } else {
+ assert(buf[0]!=0);
+ assert(buf[0]!=':');
+ }
+ assert(buf[strlen(buf)-1]=='\n');
+ } else {
+ assert(buf[0]==0);
+ }
+ if (do_errcall) {
+ assert(n_handle_error==1);
+ } else {
+ assert(n_handle_error==0);
+ }
+ fclose(read_here);
+ }
+ unlink(errfname);
+ }
+ }
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_forkjoin.cc b/storage/tokudb/PerconaFT/src/tests/test_forkjoin.cc
new file mode 100644
index 00000000000..910cf46e513
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_forkjoin.cc
@@ -0,0 +1,59 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+
+#include <toku_pthread.h>
+
+static void *
+f (void *arg) {
+ //toku_pthread_exit(arg); // toku_pthread_exit has a memory leak on linux
+ return arg;
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ toku_pthread_t t;
+ int r = toku_pthread_create(&t, 0, f, 0); assert(r == 0);
+ void *ret;
+ r = toku_pthread_join(t, &ret); assert(r == 0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_get_max_row_size.cc b/storage/tokudb/PerconaFT/src/tests/test_get_max_row_size.cc
new file mode 100644
index 00000000000..8800b7178a1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_get_max_row_size.cc
@@ -0,0 +1,74 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+int test_main(int argc, char * const argv[])
+{
+ int r;
+ DB * db;
+ DB_ENV * db_env;
+ (void) argc;
+ (void) argv;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); { int chk_r = r; CKERR(chk_r); }
+
+ // set things up
+ r = db_env_create(&db_env, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db_env->open(db_env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0755); { int chk_r = r; CKERR(chk_r); }
+ r = db_create(&db, db_env, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE, 0644); { int chk_r = r; CKERR(chk_r); }
+
+ // - does not test low bounds, so a 0 byte key is "okay"
+ // - assuming 32k keys and 32mb values are the max
+ uint32_t max_key, max_val;
+ db->get_max_row_size(db, &max_key, &max_val);
+ // assume it is a red flag for the key to be outside the 16-32kb range
+ assert(max_key >= 16*1024);
+ assert(max_key <= 32*1024);
+ // assume it is a red flag for the value to be outside the 16-32mb range
+ assert(max_val >= 16*1024*1024);
+ assert(max_val <= 32*1024*1024);
+
+ // clean things up
+ r = db->close(db, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db_env->close(db_env, 0); { int chk_r = r; CKERR(chk_r); }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_get_zeroed_dbt.cc b/storage/tokudb/PerconaFT/src/tests/test_get_zeroed_dbt.cc
new file mode 100644
index 00000000000..723b792b588
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_get_zeroed_dbt.cc
@@ -0,0 +1,80 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Test to see if DB->get works on a zeroed DBT. */
+
+#include <db.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+
+
+
+static void
+test_get (void) {
+ DB_TXN * const null_txn = 0;
+ DBT key,data;
+ char fname[] = "test.db";
+ int r;
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create (&db, env, 0); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+ dbt_init(&key, "a", 2);
+ r = db->put(db, null_txn, &key, dbt_init(&data, "b", 2), 0); assert(r==0);
+ memset(&data, 0, sizeof(data));
+ r = db->get(db, null_txn, &key, &data, 0); assert(r == 0);
+ assert(strcmp((char*)data.data, "b")==0);
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ test_get();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_groupcommit_count.cc b/storage/tokudb/PerconaFT/src/tests/test_groupcommit_count.cc
new file mode 100644
index 00000000000..414ddd2b70a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_groupcommit_count.cc
@@ -0,0 +1,220 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Test by counting the fsyncs, to see if group commit is working. */
+
+#include <db.h>
+#include <toku_pthread.h>
+#include <toku_time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+DB_ENV *env;
+DB *db;
+int do_sync=1;
+
+#define NITER 100
+
+static void *start_a_thread (void *i_p) {
+ int *CAST_FROM_VOIDP(which_thread_p, i_p);
+ int i,r;
+ for (i=0; i<NITER; i++) {
+ DB_TXN *tid;
+ char keystr[100];
+ DBT key,data;
+ snprintf(keystr, sizeof(key), "%ld.%d.%d", random(), *which_thread_p, i);
+ r=env->txn_begin(env, 0, &tid, 0); CKERR(r);
+ r=db->put(db, tid,
+ dbt_init(&key, keystr, 1+strlen(keystr)),
+ dbt_init(&data, keystr, 1+strlen(keystr)),
+ 0);
+ r=tid->commit(tid, do_sync ? 0 : DB_TXN_NOSYNC); CKERR(r);
+ }
+ return 0;
+}
+
+const char *env_path;
+
+static void
+test_groupcommit (int nthreads) {
+ int r;
+ DB_TXN *tid;
+
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, env_path, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ int i;
+ toku_pthread_t threads[nthreads];
+ int whichthread[nthreads];
+ for (i=0; i<nthreads; i++) {
+ whichthread[i]=i;
+ r=toku_pthread_create(&threads[i], 0, start_a_thread, &whichthread[i]);
+ }
+ for (i=0; i<nthreads; i++) {
+ toku_pthread_join(threads[i], 0);
+ }
+
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+
+ //if (verbose) printf(" That's a total of %d commits\n", nthreads*NITER);
+}
+
+// helgrind doesn't understand that pthread_join removes a race condition. I'm not impressed... -Bradley
+// Also, it doesn't happen every time, making helgrind unsuitable for regression tests.
+// So we must put locks around things that are properly serialized anyway.
+
+static int fsync_count_maybe_lockprotected=0;
+static void
+inc_fsync_count (void) {
+ fsync_count_maybe_lockprotected++;
+}
+
+static int
+get_fsync_count (void) {
+ int result=fsync_count_maybe_lockprotected;
+ return result;
+}
+
+static int
+do_fsync (int fd) {
+ //fprintf(stderr, "%8.6fs Thread %ld start fsyncing\n", get_tdiff(), pthread_self());
+ inc_fsync_count();
+ int r = fsync(fd);
+ //fprintf(stderr, "%8.6fs Thread %ld done fsyncing\n", get_tdiff(), pthread_self());
+ return r;
+}
+
+static const char *progname;
+static struct timeval prevtime;
+static int prev_count;
+
+static void
+printtdiff (int N) {
+ struct timeval thistime;
+ gettimeofday(&thistime, 0);
+ double diff = toku_tdiff(&thistime, &prevtime);
+ int fcount=get_fsync_count();
+ if (verbose) printf("%s: %10.6fs %4d fsyncs for %4d threads %s %8.1f tps, %8.1f tps/thread\n", progname, diff, fcount-prev_count,
+ N,
+ do_sync ? "with sync " : "with DB_TXN_NOSYNC",
+ NITER*(N/diff), NITER/diff);
+ prevtime=thistime;
+ prev_count=fcount;
+}
+
+static void
+do_test (int N) {
+ for (do_sync = 0; do_sync<2; do_sync++) {
+ int count_before = get_fsync_count();
+ test_groupcommit(N);
+ printtdiff(N);
+ int count_after = get_fsync_count();
+ if (count_after-count_before >= N*NITER) {
+ if (verbose) printf("It looks like too many fsyncs. Group commit doesn't appear to be occuring. %d - %d >= %d\n", count_after, count_before, N*NITER);
+ exit(1);
+ }
+ }
+}
+
+int log_max_n_threads_over_10 = 3;
+
+static void
+my_parse_args (int argc, char *const argv[]) {
+ verbose=1; // use -q to turn off the talking.
+ env_path = TOKU_TEST_FILENAME;
+ const char *argv0=argv[0];
+ while (argc>1) {
+ int resultcode=0;
+ if (strcmp(argv[1], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[1],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[1],"-n")==0) {
+ argc--;
+ argv++;
+ if (argc<=1) { resultcode=1; goto do_usage; }
+ errno = 0;
+ char *end;
+ log_max_n_threads_over_10 = strtol(argv[1], &end, 10);
+ if (errno!=0 || *end) {
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[1], "-h")==0) {
+ do_usage:
+ fprintf(stderr, "Usage:\n%s [-v|-q] [-n LOG(MAX_N_THREADS/10)] [-h]\n", argv0);
+ exit(resultcode);
+ } else {
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
+
+
+int
+test_main (int argc, char *const argv[]) {
+ progname=argv[0];
+ my_parse_args(argc, argv);
+
+ gettimeofday(&prevtime, 0);
+ prev_count=0;
+
+ db_env_set_func_fsync(do_fsync);
+ db_env_set_num_bucket_mutexes(32);
+
+ toku_os_recursive_delete(env_path);
+ { int r=toku_os_mkdir(env_path, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0); }
+
+ test_groupcommit(1); printtdiff(1);
+ test_groupcommit(2); printtdiff(2);
+ for (int i=0; i<log_max_n_threads_over_10; i++) {
+ do_test(10 << i);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_groupcommit_perf.cc b/storage/tokudb/PerconaFT/src/tests/test_groupcommit_perf.cc
new file mode 100644
index 00000000000..94eaf2a01a9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_groupcommit_perf.cc
@@ -0,0 +1,147 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Test using performance metrics only, to see if group commit is working. */
+
+#include <db.h>
+#include <toku_pthread.h>
+#include <toku_time.h>
+#include <sys/stat.h>
+
+DB_ENV *env;
+DB *db;
+
+#define NITER 100
+
+static void *
+start_a_thread (void *i_p) {
+ int *CAST_FROM_VOIDP(which_thread_p, i_p);
+ int i,r;
+ for (i=0; i<NITER; i++) {
+ DB_TXN *tid;
+ char keystr[100];
+ DBT key,data;
+ snprintf(keystr, sizeof(key), "%ld.%d.%d", random(), *which_thread_p, i);
+ r=env->txn_begin(env, 0, &tid, 0); CKERR(r);
+ r=db->put(db, tid,
+ dbt_init(&key, keystr, 1+strlen(keystr)),
+ dbt_init(&data, keystr, 1+strlen(keystr)),
+ 0);
+ r=tid->commit(tid, 0); CKERR(r);
+ }
+ return 0;
+}
+
+static void
+test_groupcommit (int nthreads) {
+ int r;
+ DB_TXN *tid;
+
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ int i;
+ toku_pthread_t threads[nthreads];
+ int whichthread[nthreads];
+ for (i=0; i<nthreads; i++) {
+ whichthread[i]=i;
+ r=toku_pthread_create(&threads[i], 0, start_a_thread, &whichthread[i]);
+ }
+ for (i=0; i<nthreads; i++) {
+ toku_pthread_join(threads[i], 0);
+ }
+#if 0
+ r=env->txn_begin(env, 0, &tid, 0); CKERR(r);
+ char there[1000];
+ memset(there, 'a',sizeof(there));
+ there[999]=0;
+ for (i=0; sum<(effective_max*3)/2; i++) {
+ DBT key,data;
+ char hello[20];
+ snprintf(hello, 20, "hello%d", i);
+ r=db->put(db, tid,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, sizeof(there)),
+ 0);
+ assert(r==0);
+ sum+=strlen(hello)+1+sizeof(there);
+ if ((i+1)%10==0) {
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); CKERR(r);
+ }
+ }
+ if (verbose) printf("i=%d sum=%d effmax=%d\n", i, sum, effective_max);
+ r=tid->commit(tid, 0); assert(r==0);
+#endif
+
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+
+}
+
+static struct timeval prevtime;
+
+static void
+printtdiff (const char *str) {
+ struct timeval thistime;
+ gettimeofday(&thistime, 0);
+ if (verbose) printf("%10.6f %s\n", toku_tdiff(&thistime, &prevtime), str);
+ prevtime = thistime;
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0); }
+
+ gettimeofday(&prevtime, 0);
+ test_groupcommit(1); printtdiff("1 thread");
+ test_groupcommit(2); printtdiff("2 threads");
+ test_groupcommit(10); printtdiff("10 threads");
+ test_groupcommit(20); printtdiff("20 threads");
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_hsoc.cc b/storage/tokudb/PerconaFT/src/tests/test_hsoc.cc
new file mode 100644
index 00000000000..8381c478502
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_hsoc.cc
@@ -0,0 +1,151 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+static int
+db_put (DB *db, DB_TXN *txn, int k, int v) {
+ DBT key, val;
+ int r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ return r;
+}
+
+/* create a tree with 15 of 16 leaf nodes
+ each of the leaves should be about 1/2 full
+ then almost fill leaf 0 and leaf 13 to almost full
+ reopen the tree to flush all of leaves out of the cache
+ create a cursor on leaf 0 to pull it in memory
+ fill the root buffer 13
+ insert to leaf 0. this should cause leaf 0 to split, cause the root to expand to 16 children, but
+ cause the root node to be too big. flush to leaf 16 causing another leaf split, causing the root
+ to expand to 17 nodes, which causes the root to split
+
+ the magic number where found via experimentation */
+
+static void
+test_hsoc (int pagesize) {
+ if (verbose) printf("test_hsoc:%d\n", pagesize);
+
+ int npp = pagesize / 16;
+ int n = npp + 13*npp/2;
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.hsoc.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ int i;
+
+ /* force 15 leaves (14 splits) */
+ if (verbose) printf("force15\n");
+ for (i=0; i<n; i++) {
+ r = db_put(db, null_txn, htonl(i), i); assert(r == 0);
+ }
+
+ /* almost fill leaf 0 */
+ if (verbose) printf("fill0\n");
+ for (i=0; i<(npp/2)-4; i++) {
+ r = db_put(db, null_txn, htonl(0), n+i); assert(r == 0);
+ }
+
+ /* almost fill leaf 15 */
+ if (verbose) printf("fill15\n");
+ for (i=0; i<111; i++) { // for (i=0; i<(npp/2)-4; i++) {
+ r = db_put(db, null_txn, htonl(n), i); assert(r == 0);
+ }
+
+ /* reopen the database to force nonleaf buffering */
+ if (verbose) printf("reopen\n");
+ r = db->close(db, 0); assert(r == 0);
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, 0, 0666); assert(r == 0);
+
+ /* do a cursor get k=0 to pull in leaf 0 */
+ DBC *cursor;
+
+ r = db->cursor(db, null_txn, &cursor, 0); assert(r == 0);
+
+ DBT key, val;
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_FIRST); assert(r == 0);
+ toku_free(key.data); toku_free(val.data);
+
+ /* fill up buffer 2 in the root node */
+ for (i=0; i<216; i++) {
+ r = db_put(db, null_txn, htonl(npp), i); assert(r == 0);
+ }
+
+ /* push a cmd to leaf 0 to cause it to split */
+ for (i=0; i<3; i++) {
+ r = db_put(db, null_txn, htonl(0), 2*n+i); assert(r == 0);
+ }
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ test_hsoc(4096);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_insert_cursor_delete_insert.cc b/storage/tokudb/PerconaFT/src/tests/test_insert_cursor_delete_insert.cc
new file mode 100644
index 00000000000..9af5665a7a5
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_insert_cursor_delete_insert.cc
@@ -0,0 +1,113 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_insert_delete_insert (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ if (verbose) printf("test_insert_delete_insert:\n");
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.cursor.insert.delete.insert.ft_handle";
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ DBC *cursor;
+ r = db->cursor(db, null_txn, &cursor, 0); assert(r == 0);
+
+ int k = htonl(1), v = 2;
+ DBT key, val;
+
+ r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+
+ r = cursor->c_get(cursor, dbt_init(&key, &k, sizeof k), dbt_init_malloc(&val), DB_SET);
+ assert(r == 0);
+ toku_free(val.data);
+
+ r = db->del(db, null_txn, &key, DB_DELETE_ANY); assert(r == 0);
+ assert(r == 0);
+
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_CURRENT);
+ assert(r == DB_KEYEMPTY);
+ if (key.data) toku_free(key.data);
+ if (val.data) toku_free(val.data);
+
+ r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+
+ r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_CURRENT);
+ assert(r == 0);
+ if (key.data) toku_free(key.data);
+ if (val.data) toku_free(val.data);
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ test_insert_delete_insert();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_insert_many_gc.cc b/storage/tokudb/PerconaFT/src/tests/test_insert_many_gc.cc
new file mode 100644
index 00000000000..8e5109cd2a9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_insert_many_gc.cc
@@ -0,0 +1,105 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+static void test_insert_many_gc(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_cachesize(env, 1, 0, 1); CKERR(r); // 1gb cache so this test fits in memory
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL+DB_INIT_TXN, 0); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ const int val_size = 1 * 1024 * 1024;
+
+ // Begin a snapshot transaction, which should prevent simple garbage collection
+ // from being effective. Only full garbage collection can prevent many inserts
+ // into a single leaf node from growing out of control.
+ DB_TXN *snapshot_txn;
+ r = env->txn_begin(env, NULL, &snapshot_txn, DB_TXN_SNAPSHOT); CKERR(r);
+
+ DBT key;
+ int k = 0;
+ dbt_init(&key, &k, sizeof(k));
+
+ DBT val;
+ char *XMALLOC_N(val_size, val_buf);
+ memset(val_buf, 0, val_size);
+ dbt_init(&val, val_buf, val_size);
+
+ // Keep overwriting the same row over and over.
+ const int N = 75;
+ for (int i = 0; i < N; i++) {
+ r = db->put(db, NULL, &key, &val, 0); CKERR(r);
+ }
+
+ // Full garbage collection should have prevented the leaf node
+ // from having an MVCC stack of size 'N'. At the time of this
+ // writing, we run full GC on leaf-inject when the leaf is
+ // 32mb or larger. A good invariant is that the max LE size
+ // never grew larger than 35mb and that the max commited xr stack
+ // length never exceeded 35
+ const uint64_t le_max_memsize = get_engine_status_val(env, "LE_MAX_MEMSIZE");
+ const uint64_t le_max_committed_xr = get_engine_status_val(env, "LE_MAX_COMMITTED_XR");
+ invariant(le_max_memsize <= 35 * 1024 * 1024);
+ invariant(le_max_committed_xr <= 35);
+
+ r = snapshot_txn->commit(snapshot_txn, 0); CKERR(r);
+
+ toku_free(val_buf);
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ test_insert_many_gc();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_insert_memleak.cc b/storage/tokudb/PerconaFT/src/tests/test_insert_memleak.cc
new file mode 100644
index 00000000000..9c18695c3c1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_insert_memleak.cc
@@ -0,0 +1,96 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_insert (int n, int dup_mode) {
+ if (verbose) printf("test_insert:%d %d\n", n, dup_mode);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.insert.ft_handle";
+ int r;
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_flags(db, dup_mode);
+ assert(r == 0);
+ r = db->set_pagesize(db, 4096);
+ assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ int i;
+ for (i=0; i<n; i++) {
+ int k = htonl(i);
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ test_insert(256, 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_insert_unique.cc b/storage/tokudb/PerconaFT/src/tests/test_insert_unique.cc
new file mode 100644
index 00000000000..d6913ee6095
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_insert_unique.cc
@@ -0,0 +1,159 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/**
+ * Test that unique inserts work correctly. This exercises the rightmost leaf inject optimization.
+ */
+
+#include <portability/toku_random.h>
+
+#include "test.h"
+
+static char random_buf[8];
+static struct random_data random_data;
+
+static void test_simple_unique_insert(DB_ENV *env) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE, 0644); CKERR(r);
+
+ DBT key1, key2, key3;
+ dbt_init(&key1, "a", sizeof("a"));
+ dbt_init(&key2, "b", sizeof("b"));
+ dbt_init(&key3, "c", sizeof("c"));
+ r = db->put(db, NULL, &key1, &key1, DB_NOOVERWRITE); CKERR(r);
+ r = db->put(db, NULL, &key1, &key1, DB_NOOVERWRITE); CKERR2(r, DB_KEYEXIST);
+ r = db->put(db, NULL, &key3, &key3, DB_NOOVERWRITE); CKERR(r);
+ r = db->put(db, NULL, &key3, &key3, DB_NOOVERWRITE); CKERR2(r, DB_KEYEXIST);
+ r = db->put(db, NULL, &key2, &key2, DB_NOOVERWRITE); CKERR(r);
+ r = db->put(db, NULL, &key2, &key2, DB_NOOVERWRITE); CKERR2(r, DB_KEYEXIST);
+ // sanity check
+ r = db->put(db, NULL, &key1, &key1, DB_NOOVERWRITE); CKERR2(r, DB_KEYEXIST);
+ r = db->put(db, NULL, &key1, &key3, DB_NOOVERWRITE); CKERR2(r, DB_KEYEXIST);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->dbremove(env, NULL, "db", NULL, 0); CKERR(r);
+}
+
+static void test_large_sequential_insert_unique(DB_ENV *env) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+
+ // very small nodes/basements to make a taller tree
+ r = db->set_pagesize(db, 8 * 1024); CKERR(r);
+ r = db->set_readpagesize(db, 2 * 1024); CKERR(r);
+ r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE, 0644); CKERR(r);
+
+ const int val_size = 8;
+ char *XMALLOC_N(val_size, val_buf);
+ memset(val_buf, 'k', val_size);
+ DBT val;
+ dbt_init(&val, val_buf, val_size);
+
+ // grow a tree to about depth 3, taking sanity checks along the way
+ const int start_num_rows = (64 * 1024 * 1024) / val_size;
+ for (int i = 0; i < start_num_rows; i++) {
+ DBT key;
+ int k = toku_htonl(i);
+ dbt_init(&key, &k, sizeof(k));
+ r = db->put(db, NULL, &key, &val, DB_NOOVERWRITE); CKERR(r);
+ if (i % 50 == 0) {
+ // sanity check - should not be able to insert this key twice in a row
+ r = db->put(db, NULL, &key, &val, DB_NOOVERWRITE); CKERR2(r, DB_KEYEXIST);
+
+ // .. but re-inserting is okay, if we provisionally deleted the row
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); CKERR(r);
+ r = db->put(db, txn, &key, &val, DB_NOOVERWRITE); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // re-inserting is also ok if we actually delete the row, for some key < k
+ if (i > 0) {
+ DBT other_key;
+ int other_k = toku_htonl(i - 10);
+ dbt_init(&other_key, &other_k, sizeof(other_k));
+ r = db->del(db, NULL, &other_key, DB_DELETE_ANY); CKERR(r);
+ r = db->put(db, NULL, &other_key, &val, DB_NOOVERWRITE); CKERR(r);
+ }
+ }
+ if (i > 0 && i % 250 == 0) {
+ // sanity check - unique checks on random keys we already inserted should
+ // fail (exercises middle-of-the-tree checks)
+ for (int check_i = 0; check_i < 4; check_i++) {
+ DBT rand_key;
+ int rand_k = toku_htonl(myrandom_r(&random_data) % i);
+ dbt_init(&rand_key, &rand_k, sizeof(rand_k));
+ r = db->put(db, NULL, &rand_key, &val, DB_NOOVERWRITE); CKERR2(r, DB_KEYEXIST);
+ }
+ }
+ }
+
+ toku_free(val_buf);
+ r = db->close(db, 0); CKERR(r);
+ r = env->dbremove(env, NULL, "db", NULL, 0); CKERR(r);
+}
+
+
+int test_main(int argc, char * const argv[]) {
+ default_parse_args(argc, argv);
+
+ int r;
+ const int envflags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD |
+ DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE;
+
+ // startup
+ DB_ENV *env;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); CKERR(r);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, 0755);
+
+ r = myinitstate_r(random(), random_buf, 8, &random_data); CKERR(r);
+
+ test_simple_unique_insert(env);
+ test_large_sequential_insert_unique(env);
+
+ // cleanup
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_iterate_live_transactions.cc b/storage/tokudb/PerconaFT/src/tests/test_iterate_live_transactions.cc
new file mode 100644
index 00000000000..c5561cdf90f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_iterate_live_transactions.cc
@@ -0,0 +1,137 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+static DB_TXN *txn1, *txn2, *txn3;
+static uint64_t txnid1, txnid2, txnid3;
+
+struct iterate_extra {
+ iterate_extra() : n(0) {
+ visited_txn[0] = false;
+ visited_txn[1] = false;
+ visited_txn[2] = false;
+ }
+ int n;
+ bool visited_txn[3];
+};
+
+static int iterate_callback(DB_TXN *txn,
+ iterate_row_locks_callback iterate_locks,
+ void *locks_extra, void *extra) {
+ uint64_t txnid = txn->id64(txn);
+ uint64_t client_id = txn->get_client_id(txn);
+ iterate_extra *info = reinterpret_cast<iterate_extra *>(extra);
+ DB *db;
+ DBT left_key, right_key;
+ int r = iterate_locks(&db, &left_key, &right_key, locks_extra);
+ invariant(r == DB_NOTFOUND);
+ if (txnid == txnid1) {
+ assert(!info->visited_txn[0]);
+ invariant(client_id == 0);
+ info->visited_txn[0] = true;
+ } else if (txnid == txnid2) {
+ assert(!info->visited_txn[1]);
+ invariant(client_id == 1);
+ info->visited_txn[1] = true;
+ } else if (txnid == txnid3) {
+ assert(!info->visited_txn[2]);
+ invariant(client_id == 2);
+ info->visited_txn[2] = true;
+ }
+ info->n++;
+ return 0;
+}
+
+int test_main(int UU(argc), char *const UU(argv[])) {
+ int r;
+ const int env_flags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD |
+ DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); CKERR(r);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->iterate_live_transactions(env, iterate_callback, NULL);
+ assert(r == EINVAL);
+ r = env->open(env, TOKU_TEST_FILENAME, env_flags, 0755); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn1, 0); CKERR(r);
+ txn1->set_client_id(txn1, 0);
+ txnid1 = txn1->id64(txn1);
+ r = env->txn_begin(env, NULL, &txn2, 0); CKERR(r);
+ txn2->set_client_id(txn2, 1);
+ txnid2 = txn2->id64(txn2);
+ r = env->txn_begin(env, NULL, &txn3, 0); CKERR(r);
+ txn3->set_client_id(txn3, 2);
+ txnid3 = txn3->id64(txn3);
+
+ {
+ iterate_extra e;
+ r = env->iterate_live_transactions(env, iterate_callback, &e); CKERR(r);
+ assert(e.visited_txn[0]);
+ assert(e.visited_txn[1]);
+ assert(e.visited_txn[2]);
+ assert(e.n == 3);
+ }
+
+ r = txn1->commit(txn1, 0); CKERR(r);
+ r = txn2->abort(txn2); CKERR(r);
+ {
+ iterate_extra e;
+ r = env->iterate_live_transactions(env, iterate_callback, &e); CKERR(r);
+ assert(!e.visited_txn[0]);
+ assert(!e.visited_txn[1]);
+ assert(e.visited_txn[2]);
+ assert(e.n == 1);
+ }
+
+ r = txn3->commit(txn3, 0); CKERR(r);
+ {
+ iterate_extra e;
+ r = env->iterate_live_transactions(env, iterate_callback, &e); CKERR(r);
+ assert(!e.visited_txn[0]);
+ assert(!e.visited_txn[1]);
+ assert(!e.visited_txn[2]);
+ assert(e.n == 0);
+ }
+
+ r = env->close(env, 0); CKERR(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_iterate_pending_lock_requests.cc b/storage/tokudb/PerconaFT/src/tests/test_iterate_pending_lock_requests.cc
new file mode 100644
index 00000000000..f2bf0a2c756
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_iterate_pending_lock_requests.cc
@@ -0,0 +1,134 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <portability/toku_pthread.h>
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *txn1, *txn2, *txn3;
+static const char *dname = "iterate_pending_requests_dname";
+static const int magic_key = 100;
+static int iterate_callback_called;
+toku_pthread_t thread1, thread2;
+
+// Verify the state of the world
+static int iterate_callback(DB *_db, uint64_t requesting_txnid,
+ const DBT *left_key, const DBT *right_key,
+ uint64_t blocking_txnid, uint64_t start_time, void *extra) {
+ iterate_callback_called++;
+ invariant(extra == nullptr);
+ invariant(strcmp(_db->get_dname(_db), db->get_dname(db)) == 0);
+ invariant(start_time > 0);
+ invariant(*reinterpret_cast<int *>(left_key->data) == magic_key);
+ invariant(*reinterpret_cast<int *>(right_key->data) == magic_key);
+ invariant(blocking_txnid == txn1->id64(txn1));
+ invariant(requesting_txnid == txn2->id64(txn2) || requesting_txnid == txn3->id64(txn3));
+ return 0;
+}
+
+static void acquire_lock(DB_TXN *txn, int key) {
+ int val = 0;
+ DBT k, v;
+ dbt_init(&k, &key, sizeof(int));
+ dbt_init(&v, &val, sizeof(int));
+ (void) db->put(db, txn, &k, &v, 0);
+}
+
+struct acquire_lock_extra {
+ acquire_lock_extra(DB_TXN *x, int k) :
+ txn(x), key(k) {
+ }
+ DB_TXN *txn;
+ int key;
+};
+
+static void *acquire_lock_thread(void *arg) {
+ acquire_lock_extra *info = reinterpret_cast<acquire_lock_extra *>(arg);
+ acquire_lock(info->txn, info->key);
+ return NULL;
+}
+
+int test_main(int UU(argc), char *const UU(argv[])) {
+ int r;
+ const int env_flags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD |
+ DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, env_flags, 0755); CKERR(r);
+ r = env->set_lock_timeout(env, 4000, nullptr);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, dname, NULL, DB_BTREE, DB_CREATE, 0777); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn1, DB_SERIALIZABLE); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn2, DB_SERIALIZABLE); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn3, DB_SERIALIZABLE); CKERR(r);
+
+ // Extremely simple test. Get lock [0, 0] on txn1, then asynchronously
+ // attempt to get that lock in txn2 and txn3. The iterate callback
+ // verifies that two waiters exist for [0, 0] and that txn1 is
+ // the blocking txn.
+
+ acquire_lock(txn1, magic_key);
+
+ acquire_lock_extra e1(txn2, magic_key);
+ r = toku_pthread_create(&thread1, NULL, acquire_lock_thread, &e1); CKERR(r);
+ acquire_lock_extra e2(txn3, magic_key);
+ r = toku_pthread_create(&thread2, NULL, acquire_lock_thread, &e2); CKERR(r);
+
+ sleep(1);
+ r = env->iterate_pending_lock_requests(env, iterate_callback, NULL); CKERR(r);
+ invariant(iterate_callback_called == 2);
+
+ void *v;
+ r = toku_pthread_join(thread1, &v); CKERR(r);
+ r = toku_pthread_join(thread2, &v); CKERR(r);
+
+ r = txn1->commit(txn1, 0); CKERR(r);
+ r = txn2->commit(txn2, 0); CKERR(r);
+ r = txn3->commit(txn3, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_keylen_diff.cc b/storage/tokudb/PerconaFT/src/tests/test_keylen_diff.cc
new file mode 100644
index 00000000000..10f121df7ab
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_keylen_diff.cc
@@ -0,0 +1,232 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// test a comparison function that treats certain different-lengthed keys as equal
+
+struct packed_key {
+ char type;
+ char k[8];
+ static packed_key as_int(int v) {
+ packed_key k;
+ k.type = 0;
+ memcpy(k.k, &v, sizeof(int));
+ return k;
+ }
+ static packed_key as_double(double v) {
+ packed_key k;
+ k.type = 1;
+ memcpy(k.k, &v, sizeof(double));
+ return k;
+ }
+ size_t size() const {
+ assert(type == 0 || type == 1);
+ return type == 0 ? 5 : 9;
+ }
+};
+
+// the point is that keys can be packed as integers or doubles, but
+// we'll treat them both as doubles for the sake of comparison.
+// this means a 4 byte number could equal an 8 byte number.
+static int packed_key_cmp(DB *UU(db), const DBT *a, const DBT *b) {
+ assert(a->size == 5 || a->size == 9);
+ assert(b->size == 5 || b->size == 9);
+ char *k1 = reinterpret_cast<char *>(a->data);
+ char *k2 = reinterpret_cast<char *>(b->data);
+ assert(*k1 == 0 || *k1 == 1);
+ assert(*k2 == 0 || *k2 == 1);
+ double v1 = *k1 == 0 ? static_cast<double>(*reinterpret_cast<int *>(k1 + 1)) :
+ *reinterpret_cast<double *>(k1 + 1);
+ double v2 = *k2 == 0 ? static_cast<double>(*reinterpret_cast<int *>(k2 + 1)) :
+ *reinterpret_cast<double *>(k2 + 1);
+ if (v1 > v2) {
+ return 1;
+ } else if (v1 < v2) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static int update_callback(DB *UU(db), const DBT *UU(key), const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val, void *setval_extra), void *setval_extra) {
+ assert(extra != nullptr);
+ assert(old_val != nullptr);
+ assert(extra->size == 0 || extra->size == 100);
+ assert(old_val->size == 0 || old_val->size == 100);
+ if (extra->data == nullptr) {
+ set_val(nullptr, setval_extra);
+ } else {
+ set_val(extra, setval_extra);
+ }
+ return 0;
+}
+
+enum overwrite_method {
+ VIA_UPDATE_OVERWRITE_BROADCAST,
+ VIA_UPDATE_DELETE_BROADCAST,
+ VIA_UPDATE_OVERWRITE,
+ VIA_UPDATE_DELETE,
+ VIA_DELETE,
+ VIA_INSERT,
+ NUM_OVERWRITE_METHODS
+};
+
+static void test_keylen_diff(enum overwrite_method method, bool control_test) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, packed_key_cmp); CKERR(r);
+ env->set_update(env, update_callback); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL+DB_INIT_TXN, 0); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, 16 * 1024); // smaller pages so we get a more lush tree
+ r = db->set_readpagesize(db, 1 * 1024); // smaller basements so we get more per leaf
+ r = db->open(db, nullptr, "db", nullptr, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ DBT null_dbt, val_dbt;
+ char val_buf[100];
+ memset(val_buf, 0, sizeof val_buf);
+ dbt_init(&val_dbt, &val_buf, sizeof val_buf);
+ dbt_init(&null_dbt, nullptr, 0);
+
+ const int num_keys = 1<<11; //256 * 1000;
+
+ for (int i = 0; i < num_keys; i++) {
+ // insert it using a 4 byte key ..
+ packed_key key = packed_key::as_int(i);
+
+ DBT dbt;
+ dbt_init(&dbt, &key, key.size());
+ r = db->put(db, nullptr, &dbt, &val_dbt, 0); CKERR(r);
+ }
+
+ // overwrite keys randomly, so we induce flushes and get better / realistic coverage
+ int *XMALLOC_N(num_keys, shuffled_keys);
+ for (int i = 0; i < num_keys; i++) {
+ shuffled_keys[i] = i;
+ }
+ for (int i = num_keys - 1; i >= 1; i--) {
+ long rnd = random64() % (i + 1);
+ int tmp = shuffled_keys[rnd];
+ shuffled_keys[rnd] = shuffled_keys[i];
+ shuffled_keys[i] = tmp;
+ }
+
+ for (int i = 0; i < num_keys; i++) {
+ // for the control test, delete it using the same length key
+ //
+ // .. otherwise, delete it with an 8 byte key
+ packed_key key = control_test ? packed_key::as_int(shuffled_keys[i]) :
+ packed_key::as_double(shuffled_keys[i]);
+
+ DBT dbt;
+ dbt_init(&dbt, &key, key.size());
+ DB_TXN *txn;
+ env->txn_begin(env, nullptr, &txn, DB_TXN_NOSYNC); CKERR(r);
+ switch (method) {
+ case VIA_INSERT: {
+ r = db->put(db, txn, &dbt, &val_dbt, 0); CKERR(r);
+ break;
+ }
+ case VIA_DELETE: {
+ // we purposefully do not pass DB_DELETE_ANY because the hidden query acts as
+ // a sanity check for the control test and, overall, gives better code coverage
+ r = db->del(db, txn, &dbt, 0); CKERR(r);
+ break;
+ }
+ case VIA_UPDATE_OVERWRITE:
+ case VIA_UPDATE_DELETE: {
+ r = db->update(db, txn, &dbt, method == VIA_UPDATE_DELETE ? &null_dbt : &val_dbt, 0); CKERR(r);
+ break;
+ }
+ case VIA_UPDATE_OVERWRITE_BROADCAST:
+ case VIA_UPDATE_DELETE_BROADCAST: {
+ r = db->update_broadcast(db, txn, method == VIA_UPDATE_DELETE_BROADCAST ? &null_dbt : &val_dbt, 0); CKERR(r);
+ if (i > 1 ) { // only need to test broadcast twice - one with abort, one without
+ txn->abort(txn); // we opened a txn so we should abort it before exiting
+ goto done;
+ }
+ break;
+ }
+ default: {
+ assert(false);
+ }
+ }
+ const bool abort = i % 2 == 0;
+ if (abort) {
+ txn->abort(txn);
+ } else {
+ txn->commit(txn, 0);
+ }
+ }
+
+done:
+ toku_free(shuffled_keys);
+
+ // optimize before close to ensure that all messages are applied and any potential bugs are exposed
+ r = db->optimize(db);
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ for (int i = 0; i < NUM_OVERWRITE_METHODS; i++) {
+ enum overwrite_method method = static_cast<enum overwrite_method>(i);
+
+ // control test - must pass for the 'real' test below to be interesting
+ printf("testing method %d (control)\n", i);
+ test_keylen_diff(method, true);
+
+ // real test, actually mixes key lengths
+ printf("testing method %d (real)\n", i);
+ test_keylen_diff(method, false);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_kv_gen.h b/storage/tokudb/PerconaFT/src/tests/test_kv_gen.h
new file mode 100644
index 00000000000..06f3ad3ca31
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_kv_gen.h
@@ -0,0 +1,226 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+#include "test.h"
+//
+// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
+//
+
+// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
+// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
+enum {MAX_DBS=256};
+enum {MAGIC=311};
+static int aa[MAX_DBS][32] UU();
+static int inv[MAX_DBS][32] UU();
+
+// rotate right and left functionsp
+static inline unsigned int UU()
+rotr32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x >> n) | ( x << (32 - n));
+}
+static inline unsigned int UU()
+rotl32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x << n) | ( x >> (32 - n));
+}
+
+static void UU()
+generate_permute_tables(void) {
+ srandom(1);
+ int i, j, tmp;
+ for(int db=0;db<MAX_DBS;db++) {
+ for(i=0;i<32;i++) {
+ aa[db][i] = i;
+ }
+ for(i=0;i<32;i++) {
+ j = random() % (i + 1);
+ tmp = aa[db][j];
+ aa[db][j] = aa[db][i];
+ aa[db][i] = tmp;
+ }
+ for(i=0;i<32;i++) {
+ inv[db][aa[db][i]] = i;
+ }
+ }
+}
+
+// permute bits of x based on permute table bitmap
+static unsigned int UU()
+twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << aa[db][i];
+ }
+ return b;
+}
+
+// permute bits of x based on inverse permute table bitmap
+static unsigned int UU()
+inv_twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << inv[db][i];
+ }
+ return b;
+}
+
+// generate val from key, index
+static unsigned int UU()
+generate_val(int key, int i) {
+ return rotl32((key + MAGIC), i);
+}
+static unsigned int UU()
+pkey_for_val(int key, int i) {
+ return rotr32(key, i) - MAGIC;
+}
+
+
+static int __attribute__((unused))
+dummy_progress(void *UU(extra), float UU(progress))
+{
+ return 0;
+}
+
+static void __attribute__((unused))
+do_hot_optimize_on_dbs(DB_ENV *UU(env), DB **dbs, int num_dbs)
+{
+ for (int i = 0; i < num_dbs; ++i) {
+ uint64_t loops_run;
+ int r = dbs[i]->hot_optimize(dbs[i], NULL, NULL, dummy_progress, NULL, &loops_run);
+ CKERR(r);
+ }
+}
+
+ // don't check first n rows (expect to have been deleted)
+static void UU()
+check_results_after_row_n(DB_ENV *env, DB **dbs, const int num_dbs, const int num_rows, const int first_row_to_check) {
+
+ for(int j=0;j<num_dbs;j++){
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ int r;
+ unsigned int pkey_for_db_key;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+ for(int i=first_row_to_check; i<num_rows; i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ CKERR(r);
+ k = *(unsigned int*)key.data;
+ pkey_for_db_key = (j == 0) ? k : inv_twiddle32(k, j);
+ v = *(unsigned int*)val.data;
+ // test that we have the expected keys and values
+ if ((unsigned int)pkey_for_db_key != (unsigned int)pkey_for_val(v, j))
+ printf(" DB[%d] key = %10u, val = %10u, pkey_for_db_key = %10u, pkey_for_val=%10d\n", j, v, k, pkey_for_db_key, pkey_for_val(v, j));
+ assert((unsigned int)pkey_for_db_key == (unsigned int)pkey_for_val(v, j));
+ dbt_init(&key, NULL, sizeof(unsigned int));
+ dbt_init(&val, NULL, sizeof(unsigned int));
+ }
+ if ( verbose ) {printf("."); fflush(stdout);}
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ if ( verbose ) {printf("ok");fflush(stdout);}
+}
+
+static void UU()
+check_results(DB_ENV *env, DB **dbs, const int num_dbs, const int num_rows)
+{
+ check_results_after_row_n(env, dbs, num_dbs, num_rows, 0);
+}
+
+
+static int UU()
+put_multiple_generate(DB *dest_db, DB *src_db, DBT *dest_key, DBT *dest_val, const DBT *src_key, const DBT *src_val, void *extra) {
+
+ (void) src_db;
+ (void) extra;
+
+ uint32_t which = *(uint32_t*)dest_db->app_private;
+
+ if ( which == 0 ) {
+ if (dest_key->flags==DB_DBT_REALLOC) {
+ if (dest_key->data) toku_free(dest_key->data);
+ dest_key->flags = 0;
+ dest_key->ulen = 0;
+ }
+ if (dest_val->flags==DB_DBT_REALLOC) {
+ if (dest_val->data) toku_free(dest_val->data);
+ dest_val->flags = 0;
+ dest_val->ulen = 0;
+ }
+ dbt_init(dest_key, src_key->data, src_key->size);
+ dbt_init(dest_val, src_val->data, src_val->size);
+ }
+ else {
+ assert(dest_key->flags==DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(unsigned int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(unsigned int));
+ dest_key->ulen = sizeof(unsigned int);
+ }
+ assert(dest_val->flags==DB_DBT_REALLOC);
+ if (dest_val->ulen < sizeof(unsigned int)) {
+ dest_val->data = toku_xrealloc(dest_val->data, sizeof(unsigned int));
+ dest_val->ulen = sizeof(unsigned int);
+ }
+ unsigned int *new_key = (unsigned int *)dest_key->data;
+ unsigned int *new_val = (unsigned int *)dest_val->data;
+
+ *new_key = twiddle32(*(unsigned int*)src_key->data, which);
+ *new_val = generate_val(*(unsigned int*)src_key->data, which);
+
+ dest_key->size = sizeof(unsigned int);
+ dest_val->size = sizeof(unsigned int);
+ //data is already set above
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_kv_limits.cc b/storage/tokudb/PerconaFT/src/tests/test_kv_limits.cc
new file mode 100644
index 00000000000..cd707d89fe6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_kv_limits.cc
@@ -0,0 +1,211 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static uint64_t lorange = 0;
+static uint64_t hirange = 1<<24;
+static uint32_t pagesize = 0;
+
+static void test_key_size_limit (void) {
+ if (verbose > 1) printf("%s\n", __FUNCTION__);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.rand.insert.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ void *k = 0;
+ void *v = 0;
+ uint32_t lo = lorange, mi = 0, hi = hirange;
+ uint32_t bigest = 0;
+ while (lo <= hi) {
+ mi = lo + (hi - lo) / 2;
+ assert(lo <= mi && mi <= hi);
+ uint32_t ks = mi;
+ if (verbose > 1) printf("trying %u %u %u ks=%u\n", lo, mi, hi, ks);
+ k = toku_realloc(k, ks); assert(k);
+ memset(k, 0, ks);
+ memcpy(k, &ks, sizeof ks);
+ uint32_t vs = sizeof (uint32_t);
+ v = toku_realloc(v, vs); assert(v);
+ memset(v, 0, vs);
+ memcpy(v, &vs, sizeof vs);
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, k, ks), dbt_init(&val, v, vs), 0);
+ if (r == 0) {
+ bigest = mi;
+ lo = mi+1;
+ } else {
+ if (verbose > 1) printf("%u too big\n", ks);
+ hi = mi-1;
+ }
+ }
+ toku_free(k);
+ toku_free(v);
+ assert(bigest > 0);
+ if (verbose) printf("%s bigest %u\n", __FUNCTION__, bigest);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+static void test_data_size_limit (void) {
+ if (verbose > 1) printf("%s\n", __FUNCTION__);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.rand.insert.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ if (pagesize) {
+ r = db->set_pagesize(db, pagesize); assert(r == 0);
+ }
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ void *k = 0;
+ void *v = 0;
+ uint32_t lo = lorange, mi = 0, hi = hirange;
+ uint32_t bigest = 0;
+ while (lo <= hi) {
+ mi = lo + (hi - lo) / 2;
+ assert(lo <= mi && mi <= hi);
+ uint32_t ks = sizeof (uint32_t);
+ if (verbose > 1) printf("trying %u %u %u ks=%u\n", lo, mi, hi, ks);
+ k = toku_realloc(k, ks); assert(k);
+ memset(k, 0, ks);
+ memcpy(k, &ks, sizeof ks);
+ uint32_t vs = mi;
+ v = toku_realloc(v, vs); assert(v);
+ memset(v, 0, vs);
+ memcpy(v, &vs, sizeof vs);
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, k, ks), dbt_init(&val, v, vs), 0);
+ if (r == 0) {
+ bigest = mi;
+ lo = mi+1;
+ } else {
+ if (verbose > 1) printf("%u too big\n", vs);
+ hi = mi-1;
+ }
+ }
+ toku_free(k);
+ toku_free(v);
+ if (verbose && bigest > 0) printf("%s bigest %u\n", __FUNCTION__, bigest);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ int do_key = 1;
+ int do_data = 1;
+ for (int i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-lorange") == 0 && i+1 < argc) {
+ lorange = strtoull(argv[++i], 0, 10);
+ if (lorange > ULLONG_MAX)
+ return 2;
+ continue;
+ }
+ if (strcmp(arg, "-hirange") == 0 && i+1 < argc) {
+ hirange = strtoull(argv[++i], 0, 10);
+ if (hirange > ULLONG_MAX)
+ return 2;
+ continue;
+ }
+ if (strcmp(arg, "-pagesize") == 0 && i+1 < argc) {
+ pagesize = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "-nokey") == 0) {
+ do_key = 0;
+ continue;
+ }
+ if (strcmp(arg, "-nodata") == 0) {
+ do_data = 0;
+ continue;
+ }
+ }
+
+ if (do_key)
+ test_key_size_limit();
+ if (do_data)
+ test_data_size_limit();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_large_update_broadcast_small_cachetable.cc b/storage/tokudb/PerconaFT/src/tests/test_large_update_broadcast_small_cachetable.cc
new file mode 100644
index 00000000000..a5cfac34e1a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_large_update_broadcast_small_cachetable.cc
@@ -0,0 +1,191 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// This test sets the cache size to be small and then inserts enough data
+// to make some basement nodes get evicted. Then sends a broadcast update
+// and checks all the data. If the msns for evicted basement nodes and
+// leaf nodes are not managed properly, this test should fail (because the
+// broadcast message will not be applied to basement nodes being brought
+// back in).
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = (1<<17);
+const unsigned int MAGIC_EXTRA = 0x4ac0ffee;
+
+const char original_data[] = "original: ha.rpbkasrkcabkshtabksraghpkars3cbkarpcpktkpbarkca.hpbtkvaekragptknbnsaotbknotbkaontekhba";
+const char updated_data[] = "updated: crkphi30bi8a9hpckbrap.k98a.pkrh3miachpk0[alr3s4nmubrp8.9girhp,bgoekhrl,nurbperk8ochk,bktoe";
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *e;
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ assert(*e == MAGIC_EXTRA);
+ assert(old_val->size == sizeof(original_data));
+ assert(memcmp(old_val->data, original_data, sizeof(original_data)) == 0);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, updated_data, sizeof(updated_data)), set_extra);
+ }
+
+ return 0;
+}
+
+static int
+int_cmp(DB *UU(db), const DBT *a, const DBT *b) {
+ unsigned int *ap, *bp;
+ assert(a->size == sizeof(*ap));
+ CAST_FROM_VOIDP(ap, a->data);
+ assert(b->size == sizeof(*bp));
+ CAST_FROM_VOIDP(bp, b->data);
+ return (*ap > *bp) - (*ap < *bp);
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ env->set_cachesize(env, 0, 10*(1<<20), 1);
+ { int chk_r = env->set_default_bt_compare(env, int_cmp); CKERR(chk_r); }
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, original_data, sizeof(original_data));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ unsigned int e = MAGIC_EXTRA;
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ int r = db->update_broadcast(db, txn, extrap, 0); CKERR(r);
+ return r;
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(updated_data));
+ assert(memcmp(val.data, updated_data, sizeof(updated_data)) == 0);
+ }
+ return r;
+}
+
+static int run_test(bool shutdown_before_update, bool shutdown_before_verify) {
+ setup();
+
+ DB *db;
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ db->set_pagesize(db, 256*1024);
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ if (shutdown_before_update) {
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ IN_TXN_COMMIT(env, NULL, txn_reopen, 0, {
+ { int chk_r = db->open(db, txn_reopen, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ }
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ if (shutdown_before_verify) {
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ IN_TXN_COMMIT(env, NULL, txn_reopen, 0, {
+ { int chk_r = db->open(db, txn_reopen, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ }
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+
+ run_test(false, false);
+ run_test(false, true);
+ run_test(true, false);
+ run_test(true, true);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_lock_timeout_callback.cc b/storage/tokudb/PerconaFT/src/tests/test_lock_timeout_callback.cc
new file mode 100644
index 00000000000..75ea49ec858
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_lock_timeout_callback.cc
@@ -0,0 +1,140 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <portability/toku_pthread.h>
+#include <portability/toku_atomic.h>
+
+static DB_ENV *env;
+static DB *db;
+static DB_TXN *txn1, *txn2;
+static const int magic_key = 100;
+static int callback_calls;
+toku_pthread_t thread1;
+
+static void lock_not_granted(DB *_db, uint64_t requesting_txnid,
+ const DBT *left_key, const DBT *right_key,
+ uint64_t blocking_txnid) {
+ toku_sync_fetch_and_add(&callback_calls, 1);
+ invariant(strcmp(_db->get_dname(_db), db->get_dname(db)) == 0);
+ if (requesting_txnid == txn2->id64(txn2)) {
+ invariant(blocking_txnid == txn1->id64(txn1));
+ invariant(*reinterpret_cast<int *>(left_key->data) == magic_key);
+ invariant(*reinterpret_cast<int *>(right_key->data) == magic_key);
+ } else {
+ invariant(blocking_txnid == txn2->id64(txn2));
+ invariant(*reinterpret_cast<int *>(left_key->data) == magic_key + 1);
+ invariant(*reinterpret_cast<int *>(right_key->data) == magic_key + 1);
+ }
+}
+
+static void acquire_lock(DB_TXN *txn, int key) {
+ int val = 0;
+ DBT k, v;
+ dbt_init(&k, &key, sizeof(int));
+ dbt_init(&v, &val, sizeof(int));
+ (void) db->put(db, txn, &k, &v, 0);
+}
+
+struct acquire_lock_extra {
+ acquire_lock_extra(DB_TXN *x, int k) :
+ txn(x), key(k) {
+ }
+ DB_TXN *txn;
+ int key;
+};
+
+static void *acquire_lock_thread(void *arg) {
+ acquire_lock_extra *info = reinterpret_cast<acquire_lock_extra *>(arg);
+ acquire_lock(info->txn, info->key);
+ return NULL;
+}
+
+int test_main(int UU(argc), char *const UU(argv[])) {
+ int r;
+ const int env_flags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD |
+ DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); CKERR(r);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, env_flags, 0755); CKERR(r);
+ r = env->set_lock_timeout(env, 1000, nullptr);
+ r = env->set_lock_timeout_callback(env, lock_not_granted);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "test", NULL, DB_BTREE, DB_CREATE, 0777); CKERR(r);
+
+ r = env->txn_begin(env, NULL, &txn1, DB_SERIALIZABLE); CKERR(r);
+ r = env->txn_begin(env, NULL, &txn2, DB_SERIALIZABLE); CKERR(r);
+
+ // Extremely simple test. Get lock [0, 0] on txn1, then asynchronously
+ // attempt to get that lock in txn2. The timouet callback should get called.
+
+ acquire_lock(txn1, magic_key);
+ invariant(callback_calls == 0);
+
+ acquire_lock(txn2, magic_key);
+ invariant(callback_calls == 1);
+
+ // If we enduce a deadlock, the callback should get called.
+ acquire_lock(txn2, magic_key + 1);
+ toku_pthread_t thread;
+ acquire_lock_extra e(txn1, magic_key + 1);
+ r = toku_pthread_create(&thread, NULL, acquire_lock_thread, &e);
+ usleep(100000);
+ acquire_lock(txn2, magic_key);
+ invariant(callback_calls == 2);
+ void *v;
+ r = toku_pthread_join(thread, &v); CKERR(r);
+ invariant(callback_calls == 3);
+
+ // If we set the callback to null, then it shouldn't get called anymore.
+ env->set_lock_timeout_callback(env, nullptr);
+ acquire_lock(txn2, magic_key);
+ invariant(callback_calls == 3);
+
+ r = txn1->commit(txn1, 0); CKERR(r);
+ r = txn2->commit(txn2, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_locking_with_read_txn.cc b/storage/tokudb/PerconaFT/src/tests/test_locking_with_read_txn.cc
new file mode 100644
index 00000000000..fbff18ef857
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_locking_with_read_txn.cc
@@ -0,0 +1,90 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+int test_main(int argc, char * const argv[])
+{
+ int r;
+ DB * db;
+ DB_ENV * env;
+ (void) argc;
+ (void) argv;
+
+ const char *db_env_dir = TOKU_TEST_FILENAME;
+ char rm_cmd[strlen(db_env_dir) + strlen("rm -rf ") + 1];
+ snprintf(rm_cmd, sizeof(rm_cmd), "rm -rf %s", db_env_dir);
+
+ r = system(rm_cmd); { int chk_r = r; CKERR(chk_r); }
+ r = toku_os_mkdir(db_env_dir, 0755); { int chk_r = r; CKERR(chk_r); }
+
+ // set things up
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, db_env_dir, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, 0755);
+ CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_CREATE, 0644);
+ CKERR(r);
+
+
+ DB_TXN* txn1 = NULL;
+ DB_TXN* txn2 = NULL;
+ r = env->txn_begin(env, 0, &txn1, DB_TXN_READ_ONLY);
+ CKERR(r);
+ r = env->txn_begin(env, 0, &txn2, DB_TXN_READ_ONLY);
+ CKERR(r);
+
+
+ r=db->pre_acquire_table_lock(db, txn1); CKERR(r);
+ r=db->pre_acquire_table_lock(db, txn2); CKERR2(r, DB_LOCK_NOTGRANTED);
+
+ r = txn1->commit(txn1, 0);
+ CKERR(r);
+ r = txn2->commit(txn2, 0);
+ CKERR(r);
+
+ // clean things up
+ r = db->close(db, 0);
+ CKERR(r);
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_locktree_close.cc b/storage/tokudb/PerconaFT/src/tests/test_locktree_close.cc
new file mode 100644
index 00000000000..050a5d48cbd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_locktree_close.cc
@@ -0,0 +1,115 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_cursor (void) {
+ if (verbose) printf("test_cursor\n");
+
+ DB_ENV * env;
+ DB *db;
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.cursor.ft";
+ int r;
+
+ /* create the dup database file */
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_TXN | DB_INIT_LOCK |DB_CREATE|DB_INIT_MPOOL|DB_THREAD|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ db->set_errfile(db,stderr); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ DBC* cursor;
+ DBT k0; memset(&k0, 0, sizeof k0);
+ DBT v0; memset(&v0, 0, sizeof v0);
+ DB_TXN* txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, DB_SERIALIZABLE); CKERR(r);
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ r = cursor->c_set_bounds(
+ cursor,
+ db->dbt_neg_infty(),
+ db->dbt_pos_infty(),
+ true,
+ 0
+ );
+ r = cursor->c_close(cursor); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_THREAD, 0666); assert(r == 0);
+ DB_TXN* txn2 = NULL;
+ env->txn_begin(env, NULL, &txn2, DB_SERIALIZABLE);
+ int k = htonl(1);
+ int v = htonl(1);
+ DBT key, val;
+ // #4838 will improperly allow this put to succeed, whereas we should
+ // be returning DB_LOCK_NOTGRANTED
+ r = db->put(db, txn2, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+
+ r = txn->commit(txn, 0);
+ r = txn2->commit(txn2, 0);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ test_cursor();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log0.cc b/storage/tokudb/PerconaFT/src/tests/test_log0.cc
new file mode 100644
index 00000000000..67c6c92a701
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log0.cc
@@ -0,0 +1,62 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Simple test of logging. Can I start PerconaFT with logging enabled? */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log1.cc b/storage/tokudb/PerconaFT/src/tests/test_log1.cc
new file mode 100644
index 00000000000..65fa1d830ef
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log1.cc
@@ -0,0 +1,111 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Simple test of logging. Can I start PerconaFT with logging enabled? */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <db.h>
+#include <memory.h>
+#include <stdio.h>
+#include <errno.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+DB *db;
+DB_TXN *tid;
+
+static void make_db (bool close_env) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ {
+ DBT key,data;
+ dbt_init(&key, "hello", sizeof "hello");
+ dbt_init(&data, "there", sizeof "there");
+ r=db->put(db, tid, &key, &data, 0);
+ CKERR(r);
+ }
+ char *filename;
+ {
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, "foo.db", sizeof("foo.db"));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname);
+ CKERR(r);
+ CAST_FROM_VOIDP(filename, iname.data);
+ assert(filename);
+ }
+
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ {
+ toku_struct_stat statbuf;
+ char fullfile[TOKU_PATH_MAX+1];
+ r = toku_stat(toku_path_join(fullfile, 2, TOKU_TEST_FILENAME, filename), &statbuf);
+ assert(r==0);
+ toku_free(filename);
+ }
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log10.cc b/storage/tokudb/PerconaFT/src/tests/test_log10.cc
new file mode 100644
index 00000000000..f1e3782da42
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log10.cc
@@ -0,0 +1,147 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+/* This test_log10 inserts to a db, closes, reopens, and inserts more to db. We want to make sure that the recovery of the buffers works. */
+/* Lots of stuff gets inserted. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+struct in_db;
+struct in_db {
+ long int r;
+ int i;
+ struct in_db *next;
+} *items=0;
+
+int maxcount = 10000;
+
+static void insert_some (int outeri, bool close_env) {
+ uint32_t create_flag = outeri%2 ? DB_CREATE : 0; // Sometimes use DB_CREATE, sometimes don't.
+ int r;
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ r=db_env_create(&env, 0); assert(r==0);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|create_flag, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, create_flag, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ int i;
+ for (i=0; i<maxcount; i++) {
+ char hello[30], there[30];
+ DBT key,data;
+ struct in_db *XMALLOC(newitem);
+ newitem->r = random();
+ newitem->i = i;
+ newitem->next = items;
+ items = newitem;
+ snprintf(hello, sizeof(hello), "hello%ld.%d.%d", newitem->r, outeri, newitem->i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ r=db->put(db, tid, &key, &data, 0); CKERR(r);
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+}
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); CKERR(r);
+ if (close_env) {
+ r=env->close(env, 0); CKERR(r);
+ }
+
+ for (i=0; i<10; i++)
+ insert_some(i, close_env);
+
+ while (items) {
+ struct in_db *next=items->next;
+ toku_free(items);
+ items=next;
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log1_abort.cc b/storage/tokudb/PerconaFT/src/tests/test_log1_abort.cc
new file mode 100644
index 00000000000..92c3657a154
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log1_abort.cc
@@ -0,0 +1,90 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Do test_log1, except abort instead of commit. */
+
+
+#include <db.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+DB_ENV *env;
+DB *db;
+DB_TXN *tid;
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_PRIVATE|DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ {
+ DBT key,data;
+ dbt_init(&key, "hello", sizeof "hello");
+ dbt_init(&data, "there", sizeof "there");
+ r=db->put(db, tid, &key, &data, 0);
+ CKERR(r);
+ }
+ r=db->close(db, 0);
+ assert(r==0);
+ r=tid->abort(tid);
+ assert(r==0);
+ r=env->close(env, 0);
+ assert(r==0);
+ {
+ toku_struct_stat statbuf;
+ char filename[TOKU_PATH_MAX+1];
+ r = toku_stat(toku_path_join(filename, 2, TOKU_TEST_FILENAME, "foo.db"), &statbuf);
+ assert(r==-1);
+ assert(errno==ENOENT);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log2.cc b/storage/tokudb/PerconaFT/src/tests/test_log2.cc
new file mode 100644
index 00000000000..d68047360c1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log2.cc
@@ -0,0 +1,83 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log2_abort.cc b/storage/tokudb/PerconaFT/src/tests/test_log2_abort.cc
new file mode 100644
index 00000000000..aa197d33f3e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log2_abort.cc
@@ -0,0 +1,76 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Like test_log2 except abort. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+static void make_db (void) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+}
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ make_db();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log3.cc b/storage/tokudb/PerconaFT/src/tests/test_log3.cc
new file mode 100644
index 00000000000..da5c6c23342
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log3.cc
@@ -0,0 +1,91 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ {
+ DBT key,data;
+ dbt_init(&key, "hello", sizeof "hello");
+ dbt_init(&data, "there", sizeof "there");
+ r=db->put(db, tid, &key, &data, 0); assert(r==0);
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log3_abort.cc b/storage/tokudb/PerconaFT/src/tests/test_log3_abort.cc
new file mode 100644
index 00000000000..de2793edfe3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log3_abort.cc
@@ -0,0 +1,93 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Like test_log3 except do abort */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+static void make_db (void) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ {
+ DBT key,data;
+ dbt_init(&key, "hello", sizeof "hello");
+ dbt_init(&data, "there", sizeof "there");
+ r=db->put(db, tid, &key, &data, 0); assert(r==0);
+ }
+ r=tid->abort(tid); assert(r==0);
+
+ // Now see that the string isn't there.
+ {
+ DBT key,data;
+ dbt_init(&key, "hello", sizeof "hello");
+ dbt_init(&data, NULL, 0);
+ r=db->get(db, 0, &key, &data, 0);
+ assert(r==DB_NOTFOUND);
+ }
+
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+}
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ make_db();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log4.cc b/storage/tokudb/PerconaFT/src/tests/test_log4.cc
new file mode 100644
index 00000000000..f971eddb6c4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log4.cc
@@ -0,0 +1,99 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ for (i=0; i<20000; i++) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", random(), i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ r=db->put(db, tid, &key, &data, 0); CKERR(r);
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log4_abort.cc b/storage/tokudb/PerconaFT/src/tests/test_log4_abort.cc
new file mode 100644
index 00000000000..e9b7ee88c38
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log4_abort.cc
@@ -0,0 +1,104 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Like test_log4, except abort */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+#define N 20000
+long random_nums[N];
+
+static void make_db (void) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ for (i=0; i<N; i++) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", (random_nums[i]=random()), i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ r=db->put(db, tid, &key, &data, 0); CKERR(r);
+ }
+ r=tid->abort(tid); assert(r==0);
+ for (i=0; i<N; i++) {
+ char hello[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", (random_nums[i]=random()), i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ r=db->get(db, 0, &key, &data, 0);
+ assert(r==DB_NOTFOUND);
+ }
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+}
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ make_db();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log5.cc b/storage/tokudb/PerconaFT/src/tests/test_log5.cc
new file mode 100644
index 00000000000..eeb173bede1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log5.cc
@@ -0,0 +1,118 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+struct in_db;
+struct in_db {
+ long int r;
+ int i;
+ struct in_db *next;
+} *items=0;
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ int maxcount = 24073;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ for (i=0; i<maxcount; i++) {
+ char hello[30], there[30];
+ DBT key,data;
+ struct in_db *XMALLOC(newitem);
+ newitem->r = random();
+ newitem->i = i;
+ newitem->next = items;
+ items = newitem;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", newitem->r, newitem->i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ r=db->put(db, tid, &key, &data, 0); assert(r==0);
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+ while (items) {
+ struct in_db *next=items->next;
+ toku_free(items);
+ items=next;
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log5_abort.cc b/storage/tokudb/PerconaFT/src/tests/test_log5_abort.cc
new file mode 100644
index 00000000000..790b43eb613
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log5_abort.cc
@@ -0,0 +1,123 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Like test_log5 except abort. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+struct in_db;
+struct in_db {
+ long int r;
+ int i;
+ struct in_db *next;
+} *items=0;
+
+static void make_db (void) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ int maxcount = 24073;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ for (i=0; i<maxcount; i++) {
+ char hello[30], there[30];
+ DBT key,data;
+ struct in_db *XMALLOC(newitem);
+ newitem->r = random();
+ newitem->i = i;
+ newitem->next = items;
+ items = newitem;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", newitem->r, newitem->i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ r=db->put(db, tid, &key, &data, 0); assert(r==0);
+ }
+ r=tid->abort(tid); assert(r==0);
+ {
+ struct in_db *l=items;
+ for (l=items; l; l=l->next) {
+ char hello[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", l->r, l->i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ r=db->get(db, 0, &key, &data, 0);
+ assert(r==DB_NOTFOUND);
+ }
+ }
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+ while (items) {
+ struct in_db *next=items->next;
+ toku_free(items);
+ items=next;
+ }
+}
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ make_db();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log6.cc b/storage/tokudb/PerconaFT/src/tests/test_log6.cc
new file mode 100644
index 00000000000..c8cb697b67a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log6.cc
@@ -0,0 +1,158 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+#ifndef DB_DELETE_ANY
+#define DB_DELETE_ANY 0
+#endif
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+struct in_db;
+struct in_db {
+ long int r;
+ int i;
+ struct in_db *next;
+} *items=0, *deleted_items=0;
+
+static void put_n (DB *db, DB_TXN *tid, int i) {
+ char hello[30], there[30];
+ DBT key,data;
+ struct in_db *XMALLOC(newitem);
+ newitem->r = random();
+ newitem->i = i;
+ newitem->next = items;
+ items = newitem;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", newitem->r, newitem->i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ int r=db->put(db, tid, &key, &data, 0); assert(r==0);
+}
+
+static void del_n (DB *db, DB_TXN *tid, int i) {
+ // Move it to deleted items if it is present.
+ struct in_db *present;
+ struct in_db **prevp;
+ for ((prevp=&items), (present=items);
+ present;
+ (prevp=&present->next), (present=present->next)) {
+ if (present->i==i) {
+ // Remove it
+ struct in_db *next = present->next;
+ present->next = deleted_items;
+ deleted_items = present;
+ *prevp = next;
+
+ char hello[30];
+ DBT key;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", present->r, i);
+ memset(&key, 0, sizeof(key));
+ key.data = hello; key.size = strlen(hello)+1;
+ int r = db->del(db, tid, &key, DB_DELETE_ANY); assert(r==0);
+
+ return;
+ }
+ }
+}
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ for (i=0; i<1; i++) {
+ put_n(db, tid, i);
+ if (random()%3==0) {
+ del_n(db, tid, random()%(i+1));
+ }
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+ while (items) {
+ struct in_db *next=items->next;
+ toku_free(items);
+ items=next;
+ }
+
+ while (deleted_items) {
+ struct in_db *next=deleted_items->next;
+ toku_free(deleted_items);
+ deleted_items=next;
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log6_abort.cc b/storage/tokudb/PerconaFT/src/tests/test_log6_abort.cc
new file mode 100644
index 00000000000..67a746cd1e8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log6_abort.cc
@@ -0,0 +1,163 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Like test_log6 except abort. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+#ifndef DB_DELETE_ANY
+#define DB_DELETE_ANY 0
+#endif
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+struct in_db;
+struct in_db {
+ long int r;
+ int i;
+ struct in_db *next;
+} *items=0, *deleted_items=0;
+
+static void put_n (DB *db, DB_TXN *tid, int i) {
+ char hello[30], there[30];
+ DBT key,data;
+ struct in_db *XMALLOC(newitem);
+ newitem->r = random();
+ newitem->i = i;
+ newitem->next = items;
+ items = newitem;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", newitem->r, newitem->i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ int r=db->put(db, tid, &key, &data, 0); assert(r==0);
+}
+
+static void del_n (DB *db, DB_TXN *tid, int i) {
+ // Move it to deleted items if it is present.
+ struct in_db *present;
+ struct in_db **prevp;
+ for ((prevp=&items), (present=items);
+ present;
+ (prevp=&present->next), (present=present->next)) {
+ if (present->i==i) {
+ // Remove it
+ struct in_db *next = present->next;
+ present->next = deleted_items;
+ deleted_items = present;
+ *prevp = next;
+
+ char hello[30];
+ DBT key;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", present->r, i);
+ memset(&key, 0, sizeof(key));
+ key.data = hello; key.size = strlen(hello)+1;
+ int r = db->del(db, tid, &key, DB_DELETE_ANY); assert(r==0);
+
+ return;
+ }
+ }
+}
+
+static void make_db (void) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ for (i=0; i<1; i++) {
+ put_n(db, tid, i);
+ if (random()%3==0) {
+ del_n(db, tid, random()%(i+1));
+ }
+ }
+ r=tid->abort(tid); assert(r==0);
+ {
+ struct in_db *l=items;
+ for (l=items; l; l=l->next) {
+ char hello[30];
+ DBT key,data;
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ snprintf(hello, sizeof(hello), "hello%ld.%d", l->r, i);
+ r = db->get(db, 0, &key, &data, 0);
+ assert(r==DB_NOTFOUND);
+ }
+ }
+
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+ while (items) {
+ struct in_db *next=items->next;
+ toku_free(items);
+ items=next;
+ }
+
+ while (deleted_items) {
+ struct in_db *next=deleted_items->next;
+ toku_free(deleted_items);
+ deleted_items=next;
+ }
+}
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ make_db();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log6a_abort.cc b/storage/tokudb/PerconaFT/src/tests/test_log6a_abort.cc
new file mode 100644
index 00000000000..7ca04251d3b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log6a_abort.cc
@@ -0,0 +1,338 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* Like test_log6 except abort.
+ * And abort some stuff, but not others (unlike test_log6_abort which aborts everything) */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <search.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+
+#ifndef DB_DELETE_ANY
+#define DB_DELETE_ANY 0
+#endif
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+// How many iterations are we going to do insertions and deletions. This is a bound to the number of distinct keys in the DB.
+#define N 1000
+
+static int n_keys_mentioned=0;
+static int random_keys_mentioned[N];
+
+static DB *pending_i, *pending_d, *committed;
+
+// Keep track of what's in the committed database separately
+struct pair {int x,y;};
+
+static void
+insert_in_mem (int x, int y, int *count, struct pair *pairs) {
+ assert(*count<N);
+ pairs[(*count)++]=(struct pair){x,y};
+}
+static void
+delete_in_mem (int x, int *count, struct pair *pairs) {
+ int i;
+ for (i=0; i<*count; i++) {
+ if (pairs[i].x==x) {
+ pairs[i]=pairs[--(*count)];
+ return;
+ }
+ }
+}
+
+static int com_count=0, pend_count=0, peni_count=0;
+static struct pair com_data[N], pend_data[N], peni_data[N];
+
+static void
+insert_pending (int key, int val, DB_TXN *bookx) {
+ DBT keyd,datad;
+ //printf("IP %u,%u\n", key,val);
+
+ insert_in_mem(key, val, &peni_count, peni_data);
+ pending_i->put(pending_i, bookx,
+ dbt_init(&keyd, &key, sizeof(key)),
+ dbt_init(&datad, &val, sizeof(val)),
+ 0);
+
+ delete_in_mem(key, &pend_count, pend_data);
+ pending_d->del(pending_d, bookx,
+ dbt_init(&keyd, &key, sizeof(key)),
+ 0);
+}
+
+static void put_a_random_item (DB *db, DB_TXN *tid, int i, DB_TXN *bookx) {
+ char hello[30], there[30];
+ DBT key,data;
+ int randv = myrandom();
+ random_keys_mentioned[n_keys_mentioned++] = randv;
+ insert_pending(randv, i, bookx);
+ //printf("Insert %u\n", randv);
+ snprintf(hello, sizeof(hello), "hello%d.%d", randv, i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ int r=db->put(db, tid, &key, &data, 0);
+ if (r!=0) printf("%s:%d i=%d r=%d (%s)\n", __FILE__, __LINE__, i, r, strerror(r));
+ assert(r==0);
+}
+
+static void delete_a_random_item (DB *db, DB_TXN *tid, DB_TXN *bookx) {
+ if (n_keys_mentioned==0) return;
+ int ridx = myrandom()%n_keys_mentioned;
+ int randv = random_keys_mentioned[ridx];
+ DBT keyd;
+ DBT vald;
+ //printf("Delete %u\n", randv);
+ dbt_init(&keyd, &randv, sizeof(randv));
+ dbt_init(&vald, &randv, sizeof(randv));
+
+ pending_i->del(pending_i, bookx, &keyd, 0);
+ delete_in_mem(randv, &peni_count, peni_data);
+
+ pending_d->put(pending_d, bookx, &keyd, &vald, 0);
+ insert_in_mem(randv, randv, &pend_count, pend_data);
+
+ db->del(db, tid, &keyd, DB_DELETE_ANY);
+}
+
+static void commit_items (DB_ENV *env, int UU(i)) {
+ //printf("commit_items %d\n", i);
+ DB_TXN *txn;
+ int r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ DBC *cursor;
+ r = pending_i->cursor(pending_i, txn, &cursor, 0); assert(r==0);
+ DBT k,v;
+ memset(&k,0,sizeof(k));
+ memset(&v,0,sizeof(v));
+ //printf("%d items in peni\n", peni_count);
+ while (cursor->c_get(cursor, &k, &v, DB_FIRST)==0) {
+ assert(k.size==4);
+ assert(v.size==4);
+ int ki=*(int*)k.data;
+ int vi=*(int*)v.data;
+ //printf(" put %u %u\n", ki, vi);
+ r=committed->put(committed, txn, dbt_init(&k, &ki, sizeof(ki)), dbt_init(&v, &vi, sizeof(vi)), 0);
+ insert_in_mem(ki, vi, &com_count, com_data);
+ assert(r==0);
+ r=pending_i->del(pending_i, txn, &k, 0);
+ assert(r==0);
+ }
+ r=cursor->c_close(cursor);
+ assert(r==0);
+
+ r = pending_d->cursor(pending_d, txn, &cursor, 0); assert(r==0);
+ memset(&k,0,sizeof(k));
+ memset(&v,0,sizeof(v));
+ while (cursor->c_get(cursor, &k, &v, DB_FIRST)==0) {
+ assert(k.size==4);
+ assert(v.size==4);
+ int ki=*(int*)k.data;
+ int vi=*(int*)v.data;
+ assert(ki==vi);
+ //printf(" del %u\n", ki);
+ committed->del(committed, txn, dbt_init(&k, &ki, sizeof(ki)), 0);
+ delete_in_mem(ki, &com_count, com_data);
+ // ignore result from that del
+ r=pending_d->del(pending_d, txn, &k, 0);
+ assert(r==0);
+ }
+ r=cursor->c_close(cursor);
+ assert(r==0);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void abort_items (DB_ENV *env) {
+ DB_TXN *txn;
+ int r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ //printf("abort_items\n");
+ DBC *cursor;
+ r = pending_i->cursor(pending_i, txn, &cursor, 0); assert(r==0);
+ DBT k,v;
+ memset(&k,0,sizeof(k));
+ memset(&v,0,sizeof(v));
+ while (cursor->c_get(cursor, &k, &v, DB_FIRST)==0) {
+ assert(k.size==4);
+ assert(v.size==4);
+ int ki=*(int*)k.data;
+ //printf("Deleting %u\n", ki);
+ r=pending_i->del(pending_i, txn, dbt_init(&k, &ki, sizeof(ki)), 0);
+ assert(r==0);
+ }
+ r=cursor->c_close(cursor);
+ assert(r==0);
+
+ r = pending_d->cursor(pending_d, txn, &cursor, 0); assert(r==0);
+ memset(&k,0,sizeof(k));
+ memset(&v,0,sizeof(v));
+ while (cursor->c_get(cursor, &k, &v, DB_FIRST)==0) {
+ assert(k.size==4);
+ assert(v.size==4);
+ int ki=*(int*)k.data;
+ r=pending_d->del(pending_d, txn, dbt_init(&k, &ki, sizeof(ki)), 0);
+ assert(r==0);
+ }
+ r=cursor->c_close(cursor);
+ assert(r==0);
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static int
+compare_pairs (const void *a, const void *b) {
+ return memcmp(a,b,4);
+}
+
+static void verify_items (DB_ENV *env, DB *db) {
+ DB_TXN *txn;
+ int r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ DBC *cursor;
+ DBT k,v;
+ memset(&k,0,sizeof(k));
+ memset(&v,0,sizeof(v));
+
+#if 0
+ r=db->cursor(db, txn, &cursor, 0);
+ assert(r==0);
+ while (cursor->c_get(cursor, &k, &v, DB_NEXT)==0) {
+ }
+ r=cursor->c_close(cursor);
+ assert(r==0);
+#endif
+
+ r = committed->cursor(committed, txn, &cursor, 0);
+ assert(r==0);
+ qsort(com_data, com_count, sizeof(com_data[0]), compare_pairs);
+ int curscount=0;
+ //printf(" count=%d\n", com_count);
+ while (cursor->c_get(cursor, &k, &v, DB_NEXT)==0) {
+ int kv=*(int*)k.data;
+ int dv=*(int*)v.data;
+ //printf(" sorted com_data[%d]=%d, cursor got %d\n", curscount, com_data[curscount].x, kv);
+ assert(com_data[curscount].x==kv);
+ DBT k2,v2;
+ memset(&k2, 0, sizeof(k2));
+ memset(&v2, 0, sizeof(v2));
+ char hello[30], there[30];
+ snprintf(hello, sizeof(hello), "hello%d.%d", kv, dv);
+ snprintf(there, sizeof(hello), "there%d", dv);
+ k2.data = hello; k2.size=strlen(hello)+1;
+ //printf("committed: %u,%u\n", kv, dv);
+ r=db->get(db, txn, &k2, &v2, 0);
+ assert(r==0);
+ assert(strcmp((char*)v2.data, there)==0);
+ curscount++;
+ }
+ assert(curscount==com_count);
+ r=cursor->c_close(cursor);
+ assert(r==0);
+
+ r=txn->commit(txn, 0); assert(r==0);
+}
+
+static void make_db (void) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid, *bookx;
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db_create(&pending_i, env, 0); CKERR(r);
+ r=db_create(&pending_d, env, 0); CKERR(r);
+ r=db_create(&committed, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=pending_i->open(pending_i, tid, "pending_i.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=pending_d->open(pending_d, tid, "pending_d.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=committed->open(committed, tid, "committed.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &bookx, 0); assert(r==0);
+
+ for (i=0; i<N; i++) {
+ int randv = myrandom();
+ //if (i%10000==0) printf(".");
+ if (randv%100==0) {
+ r=tid->abort(tid); assert(r==0);
+ r=bookx->commit(bookx, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &bookx, 0); assert(r==0);
+ abort_items(env);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ } else if (randv%1000==1) {
+ r=tid->commit(tid, 0); assert(r==0);
+ r=bookx->commit(bookx, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &bookx, 0); assert(r==0);
+ commit_items(env, i);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ } else if (randv%3==0) {
+ delete_a_random_item(db, tid, bookx);
+ } else {
+ put_a_random_item(db, tid, i, bookx);
+ }
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=bookx->commit(bookx, 0); assert(r==0);
+ commit_items(env, i);
+ verify_items(env, db);
+
+ r=pending_i->close(pending_i, 0); assert(r==0);
+ r=pending_d->close(pending_d, 0); assert(r==0);
+ r=committed->close(committed, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+}
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ make_db();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log7.cc b/storage/tokudb/PerconaFT/src/tests/test_log7.cc
new file mode 100644
index 00000000000..91ce714f249
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log7.cc
@@ -0,0 +1,123 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+/* This test_log7 is like test_log5 except maxcount is larger. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+struct in_db;
+struct in_db {
+ long int r;
+ int i;
+ struct in_db *next;
+} *items=0;
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ int maxcount = 100000;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ for (i=0; i<maxcount; i++) {
+ char hello[30], there[30];
+ DBT key,data;
+ struct in_db *XMALLOC(newitem);
+ newitem->r = random();
+ newitem->i = i;
+ newitem->next = items;
+ items = newitem;
+ snprintf(hello, sizeof(hello), "hello%ld.%d", newitem->r, newitem->i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ r=db->put(db, tid, &key, &data, 0); assert(r==0);
+ // BDB cannot handle this huge transaction even with a lot of locks.
+ if (i%1000==599) {
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ }
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+ while (items) {
+ struct in_db *next=items->next;
+ toku_free(items);
+ items=next;
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log8.cc b/storage/tokudb/PerconaFT/src/tests/test_log8.cc
new file mode 100644
index 00000000000..35a877be954
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log8.cc
@@ -0,0 +1,147 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+/* This test_log8 inserts to a db, closes, reopens, and inserts more to db. We want to make sure that the recovery of the buffers works. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+struct in_db;
+struct in_db {
+ long int r;
+ int i;
+ struct in_db *next;
+} *items=0;
+
+int maxcount = 10;
+
+static void insert_some (int outeri, bool close_env) {
+ uint32_t create_flag = outeri%2 ? DB_CREATE : 0; // Sometimes use DB_CREATE, sometimes don't.
+ int r;
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ r=db_env_create(&env, 0); assert(r==0);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|create_flag, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, create_flag, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ int i;
+ for (i=0; i<maxcount; i++) {
+ char hello[30], there[30];
+ DBT key,data;
+ struct in_db *XMALLOC(newitem);
+ newitem->r = random();
+ newitem->i = i;
+ newitem->next = items;
+ items = newitem;
+ snprintf(hello, sizeof(hello), "hello%ld.%d.%d", newitem->r, outeri, newitem->i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ r=db->put(db, tid, &key, &data, 0); assert(r==0);
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+}
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); CKERR(r);
+ if (close_env) {
+ r=env->close(env, 0); CKERR(r);
+ }
+
+ for (i=0; i<1; i++)
+ insert_some(i, close_env);
+
+ while (items) {
+ struct in_db *next=items->next;
+ toku_free(items);
+ items=next;
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_log9.cc b/storage/tokudb/PerconaFT/src/tests/test_log9.cc
new file mode 100644
index 00000000000..c9df578a139
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_log9.cc
@@ -0,0 +1,146 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+/* Test to see if we can do logging and recovery. */
+/* This is very specific to PerconaFT. It won't work with Berkeley DB. */
+/* This test_log8 inserts to a db, closes, reopens, and inserts more to db. We want to make sure that the recovery of the buffers works. */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <memory.h>
+
+// TOKU_TEST_FILENAME is defined in the Makefile
+
+struct in_db;
+struct in_db {
+ long int r;
+ int i;
+ struct in_db *next;
+} *items=0;
+
+int maxcount = 10;
+
+static void insert_some (int outeri, bool close_env) {
+ uint32_t create_flag = outeri%2 ? DB_CREATE : 0; // Sometimes use DB_CREATE, sometimes don't.
+ int r;
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ r=db_env_create(&env, 0); assert(r==0);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE|create_flag, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, create_flag, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+
+ int i;
+ for (i=0; i<maxcount; i++) {
+ char hello[30], there[30];
+ DBT key,data;
+ struct in_db *XMALLOC(newitem);
+ newitem->r = random();
+ newitem->i = i;
+ newitem->next = items;
+ items = newitem;
+ snprintf(hello, sizeof(hello), "hello%ld.%d.%d", newitem->r, outeri, newitem->i);
+ snprintf(there, sizeof(hello), "there%d", i);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = hello; key.size=strlen(hello)+1;
+ data.data = there; data.size=strlen(there)+1;
+ r=db->put(db, tid, &key, &data, 0); assert(r==0);
+ }
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ if (close_env) {
+ r=env->close(env, 0); assert(r==0);
+ }
+}
+
+static void make_db (bool close_env) {
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ db_env_enable_engine_status(0); // disable engine status on crash because test is expected to fail
+
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); CKERR(r);
+ if (close_env) {
+ r=env->close(env, 0); CKERR(r);
+ }
+
+ for (i=0; i<2; i++)
+ insert_some(i, close_env);
+
+ while (items) {
+ struct in_db *next=items->next;
+ toku_free(items);
+ items=next;
+ }
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ bool close_env = true;
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argv[i], "--no-shutdown") == 0)
+ close_env = false;
+ }
+ make_db(close_env);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_logflush.cc b/storage/tokudb/PerconaFT/src/tests/test_logflush.cc
new file mode 100644
index 00000000000..f92c3ec7afb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_logflush.cc
@@ -0,0 +1,97 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <db.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+// Return the offset
+static int
+grep_for_in_logs (const char *str) {
+#define lfname "log000000000000.tokulog[0-9]*"
+#define COMMAND "grep -F -q"
+ char lname[TOKU_PATH_MAX+1];
+ toku_path_join(lname, 2, TOKU_TEST_FILENAME, lfname);
+ char cmd[strlen(str) + sizeof(COMMAND " \"\" ") + TOKU_PATH_MAX];
+ int bytes = snprintf(cmd, sizeof(cmd), COMMAND " \"%s\" %s", str, lname);
+ assert(bytes>=0);
+ assert((size_t)bytes<sizeof(cmd));
+ int r = system(cmd);
+ assert(r!=-1);
+ if (r>0) r = -1;
+ return r;
+}
+
+int
+test_main (int UU(argc), char UU(*const argv[])) {
+ int r;
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ {
+ DBT key,data;
+ char hello[]="hello";
+ char there[]="there";
+ r=env->txn_begin(env, 0, &tid, 0); CKERR(r);
+ r=db->put(db, tid,
+ dbt_init(&key, hello, sizeof(hello)),
+ dbt_init(&data, there, sizeof(there)),
+ 0);
+ r=grep_for_in_logs(hello);
+ assert(r==-1);
+ r=env->log_flush(env, 0); CKERR(r);
+ r=grep_for_in_logs(hello);
+ assert(r>=0);
+ r=tid->commit(tid, 0); CKERR(r);
+ }
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_logmax.cc b/storage/tokudb/PerconaFT/src/tests/test_logmax.cc
new file mode 100644
index 00000000000..e5de0a5d906
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_logmax.cc
@@ -0,0 +1,139 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <db.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+static void
+check_logmax (int max) {
+ int any_too_big=0;
+ DIR *dir = opendir(TOKU_TEST_FILENAME);
+ struct dirent *ent;
+ while ((ent=readdir(dir))) {
+ if ((ent->d_type==DT_REG || ent->d_type==DT_UNKNOWN) && strncmp(ent->d_name, "log", 3)==0) {
+ // It is a "log*" file
+ char full_fname[TOKU_PATH_MAX+1];
+ toku_struct_stat sbuf;
+ int r = toku_stat(toku_path_join(full_fname, 2, TOKU_TEST_FILENAME, ent->d_name), &sbuf);
+ assert(r==0);
+ if (verbose)
+ printf("%s is of size %" PRId64 "\n", ent->d_name, (int64_t)sbuf.st_size);
+ if (sbuf.st_size > max) any_too_big=1;
+ }
+ }
+ assert(!any_too_big);
+ int r=closedir(dir);
+ assert(r==0);
+}
+
+static void
+test_logmax (int logmax) {
+ int r;
+ DB_ENV *env;
+ DB *db;
+ DB_TXN *tid;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+ r=db_env_create(&env, 0); assert(r==0);
+ if (logmax>0) {
+ r=env->set_lg_max(env, logmax);
+ assert(r==0);
+ }
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ {
+ uint32_t lmax;
+ r=env->get_lg_max(env, &lmax);
+ assert(r==0);
+ if (logmax>0) {
+ assert(lmax==(uint32_t)logmax);
+ } else {
+ assert(lmax>0);
+
+ }
+ }
+ r=db_create(&db, env, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &tid, 0); assert(r==0);
+ r=db->open(db, tid, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=tid->commit(tid, 0); assert(r==0);
+
+ int i;
+ int sum = 0;
+ int effective_max;
+ if (logmax>0) effective_max = logmax;
+ else {
+ effective_max = 100<<20;
+ }
+
+ r=env->txn_begin(env, 0, &tid, 0); CKERR(r);
+ char there[1000];
+ memset(there, 'a',sizeof(there));
+ there[999]=0;
+ for (i=0; sum<(effective_max*3)/2; i++) {
+ DBT key,data;
+ char hello[20];
+ snprintf(hello, 20, "hello%d", i);
+ r=db->put(db, tid,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, sizeof(there)),
+ 0);
+ assert(r==0);
+ sum+=strlen(hello)+1+sizeof(there);
+ if ((i+1)%10==0) {
+ r=tid->commit(tid, 0); assert(r==0);
+ r=env->txn_begin(env, 0, &tid, 0); CKERR(r);
+ }
+ }
+ if (verbose) printf("i=%d sum=%d effmax=%d\n", i, sum, effective_max);
+ r=tid->commit(tid, 0); assert(r==0);
+ r=db->close(db, 0); assert(r==0);
+ r=env->close(env, 0); assert(r==0);
+ check_logmax(effective_max);
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_logmax(1<<20);
+ test_logmax(-1);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_memcmp_magic.cc b/storage/tokudb/PerconaFT/src/tests/test_memcmp_magic.cc
new file mode 100644
index 00000000000..8b56e716a4a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_memcmp_magic.cc
@@ -0,0 +1,169 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include "util/dbt.h"
+
+static void test_memcmp_magic(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL+DB_INIT_TXN, 0); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+
+ // Can't set the memcmp magic to 0 (since it's used as a sentinel for `none')
+ r = db->set_memcmp_magic(db, 0); CKERR2(r, EINVAL);
+
+ // Should be ok to set it more than once, even to different things, before opening.
+ r = db->set_memcmp_magic(db, 1); CKERR(r);
+ r = db->set_memcmp_magic(db, 2); CKERR(r);
+ r = db->open(db, NULL, "db", "db", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ // Can't set the memcmp magic after opening.
+ r = db->set_memcmp_magic(db, 0); CKERR2(r, EINVAL);
+ r = db->set_memcmp_magic(db, 1); CKERR2(r, EINVAL);
+
+ DB *db2;
+ r = db_create(&db2, env, 0); CKERR(r);
+ r = db2->set_memcmp_magic(db2, 3); CKERR(r); // ..we can try setting it to something different
+ // ..but it should fail to open
+ r = db2->open(db2, NULL, "db", "db", DB_BTREE, DB_CREATE, 0666); CKERR2(r, EINVAL);
+ r = db2->set_memcmp_magic(db2, 2); CKERR(r);
+ r = db2->open(db2, NULL, "db", "db", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ r = db2->close(db2, 0);
+ r = db->close(db, 0); CKERR(r);
+
+ // dbremove opens its own handle internally. ensure that the open
+ // operation succeeds (and so does dbremove) despite the fact the
+ // internal open does not set the memcmp magic
+ r = env->dbremove(env, NULL, "db", "db", 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static int comparison_function_unused(DB *UU(db), const DBT *UU(a), const DBT *UU(b)) {
+ // We're testing that the memcmp magic gets used so the real
+ // comparison function should never get called.
+ invariant(false);
+ return 0;
+}
+
+static int getf_key_cb(const DBT *key, const DBT *UU(val), void *extra) {
+ DBT *dbt = reinterpret_cast<DBT *>(extra);
+ toku_clone_dbt(dbt, *key);
+ return 0;
+}
+
+static void test_memcmp_magic_sort_order(void) {
+ int r;
+
+ // Verify that randomly generated integer keys are sorted in memcmp
+ // order when packed as little endian, even with an environment-wide
+ // comparison function that sorts as though keys are big-endian ints.
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, comparison_function_unused); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL+DB_INIT_TXN, 0); CKERR(r);
+
+ const int magic = 49;
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_memcmp_magic(db, magic); CKERR(r);
+ r = db->open(db, NULL, "db", "db", DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ for (int i = 0; i < 10000; i++) {
+ char buf[1 + sizeof(int)];
+ // Serialize key to first have the magic byte, then the little-endian key.
+ int k = toku_htonl(random());
+ buf[0] = magic;
+ memcpy(&buf[1], &k, sizeof(int));
+
+ DBT key;
+ dbt_init(&key, buf, sizeof(buf));
+ r = db->put(db, NULL, &key, &key, 0); CKERR(r);
+ }
+
+ DB_TXN *txn;
+ env->txn_begin(env, NULL, &txn, 0);
+ DBC *dbc;
+ db->cursor(db, txn, &dbc, 0);
+ DBT prev_dbt, curr_dbt;
+ memset(&curr_dbt, 0, sizeof(DBT));
+ memset(&prev_dbt, 0, sizeof(DBT));
+ while (dbc->c_getf_next(dbc, 0, getf_key_cb, &curr_dbt)) {
+ invariant(curr_dbt.size == sizeof(int));
+ if (prev_dbt.data != NULL) {
+ // Each key should be >= to the last using memcmp
+ int c = memcmp(prev_dbt.data, curr_dbt.data, sizeof(int));
+ invariant(c <= 0);
+ }
+ toku_destroy_dbt(&prev_dbt);
+ prev_dbt = curr_dbt;
+ }
+ toku_destroy_dbt(&curr_dbt);
+ toku_destroy_dbt(&prev_dbt);
+ dbc->c_close(dbc);
+ txn->commit(txn, 0);
+
+ r = db->close(db, 0); CKERR(r);
+
+ // dbremove opens its own handle internally. ensure that the open
+ // operation succeeds (and so does dbremove) despite the fact the
+ // internal open does not set the memcmp magic
+ r = env->dbremove(env, NULL, "db", "db", 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ test_memcmp_magic();
+ test_memcmp_magic_sort_order();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_mostly_seq.cc b/storage/tokudb/PerconaFT/src/tests/test_mostly_seq.cc
new file mode 100644
index 00000000000..ecd88f3c5fe
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_mostly_seq.cc
@@ -0,0 +1,110 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <time.h>
+#include <db.h>
+
+static void
+seqinsert (int n, float p) {
+ if (verbose) printf("%s %d %f\n", __FUNCTION__, n, p);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_PRIVATE + DB_CREATE, 077); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+
+ r = db->open(db, 0, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+
+ int i;
+ for (i = 2; i <= 2*n; i += 2) {
+ int k = htonl(i);
+ int v = i;
+ DBT key, val;
+ r = db->put(db, 0, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0); assert(r == 0);
+ if (random() <= RAND_MAX * p) {
+ k = htonl(i-1);
+ v = i-1;
+ r = db->put(db, 0, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0); assert(r == 0);
+ }
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ srandom(time(0));
+ int i;
+ for (i=1; i<argc; i++) {
+ const char *arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-seed") == 0) {
+ if (i+1 >= argc) return 1;
+ srandom(atoi(argv[++i]));
+ continue;
+ }
+ }
+
+ int nodesize = 1024*1024;
+ int entrysize = 25;
+ int d = nodesize/entrysize;
+ int n = d + d/4;
+
+ float ps[] = { 0.0, 0.0001, 0.001, 0.01, 0.1, 0.25, 0.5, 1 };
+ for (i=0; i<(int)(sizeof ps / sizeof (float)); i++) {
+ seqinsert(n, ps[i]);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_multiple_checkpoints_block_commit.cc b/storage/tokudb/PerconaFT/src/tests/test_multiple_checkpoints_block_commit.cc
new file mode 100644
index 00000000000..66e9f4a5ec8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_multiple_checkpoints_block_commit.cc
@@ -0,0 +1,136 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+#include "toku_pthread.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ usleep(10*1024*1024);
+}
+
+static void *run_checkpoint(void *arg) {
+ int r = env->txn_checkpoint(env, 0, 0, 0);
+ assert_zero(r);
+ return arg;
+}
+
+static uint64_t tdelta_usec(struct timeval *tend, struct timeval *tstart) {
+ uint64_t t = tend->tv_sec * 1000000 + tend->tv_usec;
+ t -= tstart->tv_sec * 1000000 + tstart->tv_usec;
+ return t;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ DBT key, val;
+ int i = 0;
+ int v = 0;
+ dbt_init(&key, &i, sizeof(i));
+ dbt_init(&val, &v, sizeof(v));
+ // put a value to make it dirty, just to make sure that checkpoint
+ // will do something
+ { int chk_r = db->put(db, NULL, &key, &val, 0); CKERR(chk_r); }
+
+ // at this point, we have a db that is dirty. Now we want to do the following
+ // have two threads each start a checkpoint
+ // then have a third thread try to create a txn, do a write,
+ // and commit the txn. In 5.2.3, the commit of the txn would block
+ // until the one of the checkpoints complete (which should take 10 seconds)
+ // With the fix, the commit should return immedietely
+ toku_pthread_t chkpt1_tid;
+ toku_pthread_t chkpt2_tid;
+
+
+ { int chk_r = toku_pthread_create(&chkpt1_tid, NULL, run_checkpoint, NULL); CKERR(chk_r); }
+ { int chk_r = toku_pthread_create(&chkpt2_tid, NULL, run_checkpoint, NULL); CKERR(chk_r); }
+ usleep(2*1024*1024);
+ struct timeval tstart;
+ gettimeofday(&tstart, NULL);
+ DB_TXN *txn = NULL;
+ { int chk_r = env->txn_begin(env, NULL, &txn, 0); CKERR(chk_r); }
+ i = 1; v = 1;
+ { int chk_r = db->put(db, txn, &key, &val, 0); CKERR(chk_r); }
+ { int chk_r = txn->commit(txn, 0); CKERR(chk_r); }
+
+ struct timeval tend;
+ gettimeofday(&tend, NULL);
+ uint64_t diff = tdelta_usec(&tend, &tstart);
+ assert(diff < 5*1024*1024);
+
+
+ void *ret;
+ { int chk_r = toku_pthread_join(chkpt2_tid, &ret); CKERR(chk_r); }
+ { int chk_r = toku_pthread_join(chkpt1_tid, &ret); CKERR(chk_r); }
+
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+ db = NULL;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_nested.cc b/storage/tokudb/PerconaFT/src/tests/test_nested.cc
new file mode 100644
index 00000000000..1c802ab83f7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_nested.cc
@@ -0,0 +1,185 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Nested transactions. */
+
+#include <db.h>
+#include <sys/stat.h>
+
+static DB_ENV *env;
+static DB *db;
+
+static void insert (int i, DB_TXN *x) {
+ char hello[30], there[30];
+ DBT key,data;
+ if (verbose) printf("Insert %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ snprintf(there, sizeof(there), "there%d", i);
+ int r = db->put(db, x,
+ dbt_init(&key, hello, strlen(hello)+1),
+ dbt_init(&data, there, strlen(there)+1),
+ 0);
+ CKERR(r);
+}
+
+static void op_delete (int i, DB_TXN *x) {
+ char hello[30];
+ DBT key;
+ if (verbose) printf("op_delete %d\n", i);
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ int r = db->del(db, x,
+ dbt_init(&key, hello, strlen(hello)+1),
+ 0);
+ CKERR(r);
+}
+
+static void lookup (int i, DB_TXN *x, int expect) {
+ char hello[30], there[30];
+ DBT key,data;
+ snprintf(hello, sizeof(hello), "hello%d", i);
+ memset(&data, 0, sizeof(data));
+ if (verbose) printf("Looking up %d (expecting %s)\n", i, expect==0 ? "to find" : "not to find");
+ int r = db->get(db, x,
+ dbt_init(&key, hello, strlen(hello)+1),
+ &data,
+ 0);
+ assert(expect==r);
+ if (expect==0) {
+ CKERR(r);
+ snprintf(there, sizeof(there), "there%d", i);
+ assert(data.size==strlen(there)+1);
+ assert(strcmp((char*)data.data, there)==0);
+ }
+}
+
+static DB_TXN *txn, *txn2;
+
+static void
+test_nested (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ r=db_env_create(&env, 0); assert(r==0);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ r=db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ insert(0, txn);
+
+ insert(1, txn);
+ insert(2, txn);
+ insert(3, txn);
+ lookup(0, txn, 0);
+ lookup(1, txn, 0);
+ lookup(2, txn, 0);
+ lookup(3, txn, 0);
+ r=txn->commit(txn, 0); assert(r==0);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ op_delete(0, txn);
+ op_delete(3, txn);
+ r=env->txn_begin(env, txn, &txn2, 0); CKERR(r);
+ op_delete(1, txn2); CKERR(r);
+ lookup(3, txn2, DB_NOTFOUND);
+ insert(3, txn2);
+ lookup(3, txn2, 0);
+ r=txn2->commit(txn2, 0); CKERR(r);
+ lookup(0, txn, DB_NOTFOUND);
+ lookup(1, txn, DB_NOTFOUND);
+ lookup(2, txn, 0);
+ lookup(3, txn, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ lookup(0, txn, DB_NOTFOUND);
+ lookup(1, txn, DB_NOTFOUND);
+ lookup(2, txn, 0);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(4, txn);
+ r=txn->commit(txn, 0); CKERR(r);
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=env->txn_begin(env, txn, &txn2, 0); CKERR(r);
+ op_delete(4, txn2);
+ r=txn->commit(txn2, 0); CKERR(r);
+ lookup(4, txn, DB_NOTFOUND);
+ insert(4, txn);
+ r=txn->commit(txn, 0); CKERR(r);
+ lookup(4, 0, 0);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(5, txn);
+ r=env->txn_begin(env, txn, &txn2, 0); CKERR(r);
+ lookup(5, txn2, 0);
+ insert(5, txn2);
+ lookup(5, txn2, 0);
+ r=txn->commit(txn2, 0); CKERR(r);
+ lookup(5, txn, 0);
+ r=env->txn_begin(env, txn, &txn2, 0); CKERR(r);
+ lookup(5, txn2, 0);
+ op_delete(5, txn2);
+ r=txn->commit(txn2, 0); CKERR(r);
+ lookup(5, txn, DB_NOTFOUND);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(6, txn);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ insert(6, txn);
+ r=env->txn_begin(env, txn, &txn2, 0); CKERR(r);
+ op_delete(6, txn2);
+ r=txn->commit(txn2, 0); CKERR(r);
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_nested();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_nodup_set.cc b/storage/tokudb/PerconaFT/src/tests/test_nodup_set.cc
new file mode 100644
index 00000000000..0aa94550a59
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_nodup_set.cc
@@ -0,0 +1,208 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+
+
+static void
+db_put (DB *db, int k, int v) {
+ DB_TXN * const null_txn = 0;
+ DBT key, val;
+ int r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+}
+
+static void
+db_del (DB *db, int k) {
+ DB_TXN * const null_txn = 0;
+ DBT key;
+ int r = db->del(db, null_txn, dbt_init(&key, &k, sizeof k), 0);
+ assert(r == 0);
+}
+
+static void
+expect_db_get (DB *db, int k, int v) {
+ DB_TXN * const null_txn = 0;
+ DBT key, val;
+ int r = db->get(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init_malloc(&val), 0);
+ assert(r == 0);
+ int vv;
+ assert(val.size == sizeof vv);
+ memcpy(&vv, val.data, val.size);
+ assert(vv == v);
+ toku_free(val.data);
+}
+
+static void
+expect_cursor_get (DBC *cursor, int k, int v) {
+ DBT key, val;
+ int r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_NEXT);
+ assert(r == 0);
+ assert(key.size == sizeof k);
+ int kk;
+ memcpy(&kk, key.data, key.size);
+ assert(val.size == sizeof v);
+ int vv;
+ memcpy(&vv, val.data, val.size);
+ if (kk != k || vv != v) printf("expect key %u got %u - %u %u\n", (uint32_t)htonl(k), (uint32_t)htonl(kk), (uint32_t)htonl(v), (uint32_t)htonl(vv));
+ assert(kk == k);
+ assert(vv == v);
+
+ toku_free(key.data);
+ toku_free(val.data);
+}
+
+static void
+expect_cursor_set (DBC *cursor, int k, int expectr) {
+ DBT key, val;
+ int r = cursor->c_get(cursor, dbt_init(&key, &k, sizeof k), dbt_init_malloc(&val), DB_SET);
+ assert(r == expectr);
+ if (val.data) toku_free(val.data);
+}
+
+static void
+expect_cursor_get_current (DBC *cursor, int k, int v) {
+ DBT key, val;
+ int r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_CURRENT);
+ assert(r == 0);
+ int kk, vv;
+ assert(key.size == sizeof kk); memcpy(&kk, key.data, key.size); assert(kk == k);
+ assert(val.size == sizeof vv); memcpy(&vv, val.data, val.size); assert(vv == v);
+ toku_free(key.data); toku_free(val.data);
+}
+
+
+/* insert, close, delete, insert, search */
+static void
+test_icdi_search (int n, int dup_mode) {
+ if (verbose) printf("test_icdi_search:%d %d\n", n, dup_mode);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test_icdi_search.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_flags(db, dup_mode); assert(r == 0);
+ r = db->set_pagesize(db, 4096); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ int i;
+ for (i=0; i<n; i++) {
+ int k = htonl(i);
+ int v = htonl(i);
+ db_put(db, k, v);
+
+ expect_db_get(db, k, v);
+ }
+
+ /* reopen the database to force nonleaf buffering */
+ r = db->close(db, 0); assert(r == 0);
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_flags(db, dup_mode); assert(r == 0);
+ r = db->set_pagesize(db, 4096); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, 0, 0666); assert(r == 0);
+
+ for (i=0; i<n; i++)
+ db_del(db, htonl(i));
+
+ {
+ DBC *cursor;
+ r = db->cursor(db, 0, &cursor, 0); assert(r == 0);
+ expect_cursor_set(cursor, 0, DB_NOTFOUND);
+ r = cursor->c_close(cursor); assert(r == 0);
+ }
+
+ for (i=0; i<n; i++) {
+ int k = htonl(i);
+ int v = htonl(n+i);
+ db_put(db, k, v);
+
+ DBC *cursor;
+ r = db->cursor(db, 0, &cursor, 0); assert(r == 0);
+ expect_cursor_set(cursor, k, 0);
+ expect_cursor_get_current(cursor, k, v);
+ r = cursor->c_close(cursor); assert(r == 0);
+ }
+
+ DBC *cursor;
+ r = db->cursor(db, null_txn, &cursor, 0); assert(r == 0);
+
+ for (i=0; i<n; i++) {
+ expect_cursor_get(cursor, htonl(i), htonl(n+i));
+ }
+
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ int i;
+
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ for (i=1; i<65537; i *= 2) {
+ test_icdi_search(i, 0);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_query.cc b/storage/tokudb/PerconaFT/src/tests/test_query.cc
new file mode 100644
index 00000000000..fa835b419bd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_query.cc
@@ -0,0 +1,433 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/**
+ * Test that various queries behave correctly
+ *
+ * Zardosht says:
+ *
+ * write a test that inserts a bunch of elements into the tree,
+ * and then verify that the following types of queries work:
+ * - db->get
+ * - next
+ * - prev
+ * - set_range
+ * - set_range_reverse
+ * - first
+ * - last
+ * - current
+ *
+ * do it on a table with:
+ * - just a leaf node
+ * - has internal nodes (make node size 4K and bn size 1K)
+ * - big cachetable such that everything fits
+ * - small cachetable such that not a lot fits
+ *
+ * make sure APIs are the callback APIs (getf_XXX)
+ * make sure your callbacks all return TOKUDB_CURSOR_CONTINUE,
+ * so we ensure that returning TOKUDB_CURSOR_CONTINUE does not
+ * mess anything up.
+ */
+
+#include "test.h"
+
+enum cursor_type {
+ FIRST,
+ LAST,
+ NEXT,
+ PREV,
+ CURRENT,
+ SET,
+ SET_RANGE,
+ SET_RANGE_REVERSE
+};
+
+/**
+ * Calculate or verify that a value for a given key is correct
+ * Returns 0 if the value is correct, nonzero otherwise.
+ */
+static void get_value_by_key(DBT * key, DBT * value)
+{
+ // keys/values are always stored in the DBT in net order
+ int * CAST_FROM_VOIDP(k, key->data);
+ int v = toku_ntohl(*k) * 2 + 1;
+ memcpy(value->data, &v, sizeof(int));
+}
+
+static void verify_value_by_key(DBT * key, DBT * value)
+{
+ assert(key->size == sizeof(int));
+ assert(value->size == sizeof(int));
+
+ int expected;
+ DBT expected_dbt;
+ expected_dbt.data = &expected;
+ expected_dbt.size = sizeof(int);
+ get_value_by_key(key, &expected_dbt);
+
+ int * CAST_FROM_VOIDP(v, value->data);
+ assert(*v == expected);
+}
+
+/**
+ * Callback for cursors, can be either traversing
+ * forward, backward, or not at all.
+ */
+struct cursor_cb_info {
+ int last_key_seen;
+ enum cursor_type type;
+};
+static int cursor_cb(DBT const * key,
+ DBT const * value, void * extra)
+{
+ struct cursor_cb_info * CAST_FROM_VOIDP(info, extra);
+ int * CAST_FROM_VOIDP(kbuf, key->data);
+ int k = ntohl(*kbuf);
+
+ switch (info->type) {
+ // point queries, just verify the pair
+ // is correct.
+ case SET:
+ case SET_RANGE:
+ case SET_RANGE_REVERSE:
+ case FIRST:
+ case LAST:
+ case CURRENT:
+ verify_value_by_key((DBT *) key, (DBT *) value);
+ break;
+ case NEXT:
+ // verify the last key we saw was the previous
+ verify_value_by_key((DBT *) key, (DBT *) value);
+ if (k < 0) {
+ assert(k == info->last_key_seen - 1);
+ }
+ break;
+ case PREV:
+ // verify the last key we saw was the next
+ verify_value_by_key((DBT *) key, (DBT *) value);
+ if (k < info->last_key_seen) {
+ assert(k == info->last_key_seen - 1);
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ info->last_key_seen = k;
+ return TOKUDB_CURSOR_CONTINUE;
+}
+
+/**
+ * Fill a FT with the the given number of rows.
+ */
+static void fill_db(DB_ENV * env, DB * db, int num_rows)
+{
+ int r;
+ DB_TXN * txn;
+ DBT key, value;
+
+ printf("filling db\n");
+
+ int i, j;
+ const int ins_per_txn = 1000;
+ assert(num_rows % ins_per_txn == 0);
+ for (i = 0; i < num_rows; i+= ins_per_txn) {
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ for (j = 0; j < ins_per_txn && (i + j) < num_rows; j++) {
+ int v, k = toku_htonl(i + j);
+ dbt_init(&key, &k, sizeof(int));
+ dbt_init(&value, &v, sizeof(int));
+ get_value_by_key(&key, &value);
+ r = db->put(db, txn, &key, &value, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+}
+
+static void init_env(DB_ENV ** env, size_t ct_size)
+{
+ int r;
+ const int envflags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD |
+ DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE;
+
+ printf("initializing environment\n");
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); { int chk_r = r; CKERR(chk_r); }
+
+ r = db_env_create(env, 0); { int chk_r = r; CKERR(chk_r); }
+ assert(ct_size < 1024 * 1024 * 1024L);
+ r = (*env)->set_cachesize(*env, 0, ct_size, 1); { int chk_r = r; CKERR(chk_r); }
+ r = (*env)->open(*env, TOKU_TEST_FILENAME, envflags, 0755); { int chk_r = r; CKERR(chk_r); }
+}
+
+static void init_db(DB_ENV * env, DB ** db)
+{
+ int r;
+ const int node_size = 4096;
+ const int bn_size = 1024;
+
+ printf("initializing db\n");
+
+ DB_TXN * txn;
+ r = db_create(db, env, 0); { int chk_r = r; CKERR(chk_r); }
+ r = (*db)->set_readpagesize(*db, bn_size); { int chk_r = r; CKERR(chk_r); }
+ r = (*db)->set_pagesize(*db, node_size); { int chk_r = r; CKERR(chk_r); }
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = (*db)->open(*db, txn, "db", NULL, DB_BTREE, DB_CREATE, 0644); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+}
+
+static void cleanup_env_and_db(DB_ENV * env, DB * db)
+{
+ int r;
+
+ printf("cleaning up environment and db\n");
+ r = db->close(db, 0); { int chk_r = r; CKERR(chk_r); }
+ r = env->close(env, 0); { int chk_r = r; CKERR(chk_r); }
+}
+
+static void do_test(size_t ct_size, int num_keys)
+{
+ int i, r;
+ DB * db;
+ DB_ENV * env;
+
+ printf("doing tests for ct_size %lu, num_keys %d\n",
+ ct_size, num_keys);
+
+ // initialize everything and insert data
+ init_env(&env, ct_size);
+ assert(env != NULL);
+ init_db(env, &db);
+ assert(db != NULL);
+ fill_db(env, db, num_keys);
+
+ const int last_key = num_keys - 1;
+
+ // test c_getf_first
+ printf("testing c getf first\n");
+ {
+ DBC * dbc;
+ DB_TXN * txn;
+ struct cursor_cb_info info;
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db->cursor(db, txn, &dbc, 0); { int chk_r = r; CKERR(chk_r); }
+ info.last_key_seen = -1;
+ info.type = FIRST;
+ r = dbc->c_getf_first(dbc, 0, cursor_cb, &info); { int chk_r = r; CKERR(chk_r); }
+ assert(info.last_key_seen == 0);
+ r = dbc->c_close(dbc); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+
+ // test c_getf_last
+ printf("testing c getf last\n");
+ {
+ DBC * dbc;
+ DB_TXN * txn;
+ struct cursor_cb_info info;
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db->cursor(db, txn, &dbc, 0); { int chk_r = r; CKERR(chk_r); }
+ info.last_key_seen = -1;
+ info.type = LAST;
+ r = dbc->c_getf_last(dbc, 0, cursor_cb, &info); { int chk_r = r; CKERR(chk_r); }
+ assert(info.last_key_seen == last_key);
+ r = dbc->c_close(dbc); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+
+ // test c_getf_next
+ printf("testing c getf next\n");
+ {
+ DBC * dbc;
+ DB_TXN * txn;
+ struct cursor_cb_info info;
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db->cursor(db, txn, &dbc, 0); { int chk_r = r; CKERR(chk_r); }
+ info.last_key_seen = -1;
+ //info.type = FIRST;
+ //r = dbc->c_getf_first(dbc, 0, cursor_cb, &info); { int chk_r =
+ //r; CKERR(chk_r); }
+ //assert(info.last_key_seen == 0);
+ info.type = NEXT;
+ while ((r = dbc->c_getf_next(dbc, 0, cursor_cb, &info)) == 0);
+ assert(r == DB_NOTFOUND);
+ if (info.last_key_seen != last_key) {
+ printf("last keen seen %d, wanted %d\n",
+ info.last_key_seen, last_key);
+ }
+ assert(info.last_key_seen == last_key);
+ r = dbc->c_close(dbc); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+
+ // test c_getf_prev
+ printf("testing c getf prev\n");
+ {
+ DBC * dbc;
+ DB_TXN * txn;
+ struct cursor_cb_info info;
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db->cursor(db, txn, &dbc, 0); { int chk_r = r; CKERR(chk_r); }
+ info.last_key_seen = -1;
+ //info.type = LAST;
+ //r = dbc->c_getf_last(dbc, 0, cursor_cb, &info); { int chk_r = r;
+ //CKERR(chk_r); }
+ //assert(info.last_key_seen == last_key);
+ info.type = PREV;
+ while ((r = dbc->c_getf_prev(dbc, 0, cursor_cb, &info)) == 0);
+ assert(r == DB_NOTFOUND);
+ assert(info.last_key_seen == 0);
+ r = dbc->c_close(dbc); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+
+ printf("testing db->get, c getf set, current\n");
+ {
+ DBC * dbc;
+ DB_TXN * txn;
+ struct cursor_cb_info info;
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db->cursor(db, txn, &dbc, 0); { int chk_r = r; CKERR(chk_r); }
+ for (i = 0; i < 1000; i++) {
+ DBT key;
+ int k = random() % num_keys;
+ int nk = toku_htonl(k);
+ dbt_init(&key, &nk, sizeof(int));
+
+ // test c_getf_set
+ info.last_key_seen = -1;
+ info.type = SET;
+ r = dbc->c_getf_set(dbc, 0, &key, cursor_cb, &info); { int chk_r = r; CKERR(chk_r); }
+ assert(info.last_key_seen == k);
+
+ // test c_getf_current
+ info.last_key_seen = -1;
+ info.type = CURRENT;
+ r = dbc->c_getf_current(dbc, 0, cursor_cb, &info); { int chk_r = r; CKERR(chk_r); }
+ assert(info.last_key_seen == k);
+
+ // test db->get (point query)
+ DBT value;
+ memset(&value, 0, sizeof(DBT));
+ r = db->get(db, txn, &key, &value, 0); { int chk_r = r; CKERR(chk_r); }
+ verify_value_by_key(&key, &value);
+ }
+ r = dbc->c_close(dbc); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+
+ // delete some elements over a variable stride,
+ // this will let us test range/reverse
+ const int stride = num_keys / 10;
+ printf("deleting some elements in stride %d\n", stride);
+ {
+ DBC * dbc;
+ DB_TXN * txn;
+ DBT key;
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db->cursor(db, txn, &dbc, 0); { int chk_r = r; CKERR(chk_r); }
+ for (i = 0; i < num_keys; i += stride) {
+ int k = toku_htonl(i);
+ dbt_init(&key, &k, sizeof(int));
+ r = db->del(db, txn, &key, 0);
+ }
+ r = dbc->c_close(dbc); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+
+ // test getf set range and range reverse
+ printf("testing getf set range and range reverse\n");
+ {
+ DBC * dbc;
+ DB_TXN * txn;
+ DBT key;
+ struct cursor_cb_info info;
+ r = env->txn_begin(env, NULL, &txn, 0); { int chk_r = r; CKERR(chk_r); }
+ r = db->cursor(db, txn, &dbc, 0); { int chk_r = r; CKERR(chk_r); }
+ for (i = 0; i < num_keys; i += stride) {
+ int k = toku_htonl(i);
+ dbt_init(&key, &k, sizeof(int));
+
+ // we should have only actually seen the next
+ // key after i if i was not the last key,
+ // otherwise there's nothing after that key
+ info.last_key_seen = -1;
+ info.type = SET_RANGE;
+ r = dbc->c_getf_set_range(dbc, 0, &key, cursor_cb, &info);
+ if (i == last_key) {
+ assert(r == DB_NOTFOUND);
+ } else {
+ assert(info.last_key_seen == i + 1);
+ }
+
+ // we should have only actually seen the prev
+ // key if i was not the first key, otherwise
+ // there's nothing before that key.
+ info.last_key_seen = -1;
+ info.type = SET_RANGE_REVERSE;
+ r = dbc->c_getf_set_range_reverse(dbc, 0, &key, cursor_cb, &info);
+ if (i == 0) {
+ assert(r == DB_NOTFOUND);
+ } else {
+ assert(info.last_key_seen == i - 1);
+ { int chk_r = r; CKERR(chk_r); }
+ }
+ }
+ r = dbc->c_close(dbc); { int chk_r = r; CKERR(chk_r); }
+ r = txn->commit(txn, 0); { int chk_r = r; CKERR(chk_r); }
+ }
+
+ cleanup_env_and_db(env, db);
+}
+
+int test_main(int argc, char * const argv[])
+{
+ default_parse_args(argc, argv);
+
+ // just a leaf, fits in cachetable
+ do_test(1L*1024*1024, 1000);
+ // with internal nodes, fits in cachetable
+ do_test(4L*1024*1024, 100000);
+ // with internal nodes, does not fit in cachetable
+ do_test(1L*1024*1024, 1000000);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_rand_insert.cc b/storage/tokudb/PerconaFT/src/tests/test_rand_insert.cc
new file mode 100644
index 00000000000..a9b77f8e678
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_rand_insert.cc
@@ -0,0 +1,133 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_rand_insert (int n, int dup_mode) {
+ if (verbose) printf("test_rand_insert:%d %d\n", n, dup_mode);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.rand.insert.ft_handle";
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_flags(db, dup_mode);
+ assert(r == 0);
+ r = db->set_pagesize(db, 4096);
+ assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ unsigned int keys[n];
+ int i;
+ for (i=0; i<n; i++)
+ keys[i] = htonl(random());
+
+ /* insert n/2 <random(), i> pairs */
+ for (i=0; i<n/2; i++) {
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &keys[i], sizeof keys[i]), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ /* reopen the database to force nonleaf buffering */
+ r = db->close(db, 0);
+ assert(r == 0);
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_flags(db, dup_mode);
+ assert(r == 0);
+ r = db->set_pagesize(db, 4096);
+ assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, 0, 0666);
+ assert(r == 0);
+
+ /* insert n/2 <random(), i> pairs */
+ for (i=n/2; i<n; i++) {
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &keys[i], sizeof keys[i]), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ for (i=0; i<n; i++) {
+ DBT key, val;
+ r = db->get(db, 0, dbt_init(&key, &keys[i], sizeof keys[i]), dbt_init_malloc(&val), 0);
+ assert(r == 0);
+ int vv;
+ assert(val.size == sizeof vv);
+ memcpy(&vv, val.data, val.size);
+ if (vv != i) assert(keys[vv] == keys[i]);
+ else assert(vv == i);
+ toku_free(val.data);
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int i;
+ for (i = 1; i <= 2048; i *= 2) {
+ test_rand_insert(i, 0);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_read_txn_invalid_ops.cc b/storage/tokudb/PerconaFT/src/tests/test_read_txn_invalid_ops.cc
new file mode 100644
index 00000000000..d0f7d3aab03
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_read_txn_invalid_ops.cc
@@ -0,0 +1,196 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *UU(old_val), const DBT *UU(extra),
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *UU(set_extra))
+{
+ abort();
+ assert(set_val != NULL);
+ return 0;
+}
+
+static int generate_row_for_put(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *UU(dest_key_arrays),
+ DBT_ARRAY *UU(dest_val_arrays),
+ const DBT *UU(src_key),
+ const DBT *UU(src_val)
+ )
+{
+ abort();
+ return 0;
+}
+
+static int generate_row_for_del(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *UU(dest_key_arrays),
+ const DBT *UU(src_key),
+ const DBT *UU(src_val)
+ )
+{
+ abort();
+ return 0;
+}
+
+static void test_invalid_ops(uint32_t iso_flags) {
+ int r;
+ DB * db;
+ DB_ENV * env;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); { int chk_r = r; CKERR(chk_r); }
+
+ // set things up
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->set_generate_row_callback_for_put(env,generate_row_for_put);
+ CKERR(r);
+ r = env->set_generate_row_callback_for_del(env,generate_row_for_del);
+ CKERR(r);
+ env->set_update(env, update_fun);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, 0755);
+ CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+
+ DB_TXN* txn = NULL;
+ r = env->txn_begin(env, 0, &txn, iso_flags | DB_TXN_READ_ONLY);
+ CKERR(r);
+
+ r = db->open(db, txn, "foo.db", NULL, DB_BTREE, DB_CREATE, 0644);
+ CKERR2(r, EINVAL);
+ r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, DB_CREATE, 0644);
+ CKERR(r);
+
+ int k = 1;
+ int v = 10;
+ DBT key, val;
+ dbt_init(&key, &k, sizeof k);
+ dbt_init(&val, &v, sizeof v);
+
+ uint32_t db_flags = 0;
+ uint32_t indexer_flags = 0;
+ DB_INDEXER* indexer;
+ r = env->create_indexer(
+ env,
+ txn,
+ &indexer,
+ db,
+ 1,
+ &db,
+ &db_flags,
+ indexer_flags
+ );
+ CKERR2(r, EINVAL);
+
+
+ // test invalid operations of ydb_db.cc,
+ // db->open tested above
+ DB_LOADER* loader;
+ uint32_t put_flags = 0;
+ uint32_t dbt_flags = 0;
+ r = env->create_loader(env, txn, &loader, NULL, 1, &db, &put_flags, &dbt_flags, 0);
+ CKERR2(r, EINVAL);
+
+ r = db->change_descriptor(db, txn, &key, 0);
+ CKERR2(r, EINVAL);
+
+ //
+ // test invalid operations return EINVAL from ydb_write.cc
+ //
+ r = db->put(db, txn, &key, &val,0);
+ CKERR2(r, EINVAL);
+ r = db->del(db, txn, &key, DB_DELETE_ANY);
+ CKERR2(r, EINVAL);
+ r = db->update(db, txn, &key, &val, 0);
+ CKERR2(r, EINVAL);
+ r = db->update_broadcast(db, txn, &val, 0);
+ CKERR2(r, EINVAL);
+
+ r = env_put_multiple_test_no_array(env, NULL, txn, &key, &val, 1, &db, &key, &val, 0);
+ CKERR2(r, EINVAL);
+ r = env_del_multiple_test_no_array(env, NULL, txn, &key, &val, 1, &db, &key, 0);
+ CKERR2(r, EINVAL);
+ uint32_t flags;
+ r = env_update_multiple_test_no_array(
+ env, NULL, txn,
+ &key, &val,
+ &key, &val,
+ 1, &db, &flags,
+ 1, &key,
+ 1, &val
+ );
+ CKERR2(r, EINVAL);
+
+ r = db->close(db, 0);
+ CKERR(r);
+
+ // test invalid operations of ydb.cc, dbrename and dbremove
+ r = env->dbremove(env, txn, "foo.db", NULL, 0);
+ CKERR2(r, EINVAL);
+ // test invalid operations of ydb.cc, dbrename and dbremove
+ r = env->dbrename(env, txn, "foo.db", NULL, "bar.db", 0);
+ CKERR2(r, EINVAL);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ // clean things up
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+
+int test_main(int argc, char * const argv[]) {
+ (void) argc;
+ (void) argv;
+ test_invalid_ops(0);
+ test_invalid_ops(DB_TXN_SNAPSHOT);
+ test_invalid_ops(DB_READ_COMMITTED);
+ test_invalid_ops(DB_READ_UNCOMMITTED);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_redirect_func.cc b/storage/tokudb/PerconaFT/src/tests/test_redirect_func.cc
new file mode 100644
index 00000000000..9e00bb13b68
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_redirect_func.cc
@@ -0,0 +1,188 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *UU(old_val), const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ set_val(extra, set_extra);
+ return 0;
+}
+
+
+static int generate_row_for_del(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *dest_key_arrays,
+ const DBT *UU(src_key),
+ const DBT *UU(src_val)
+ )
+{
+ toku_dbt_array_resize(dest_key_arrays, 0);
+ assert(false);
+ return 0;
+}
+
+static int generate_row_for_put(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *dest_key_arrays,
+ DBT_ARRAY *dest_val_arrays,
+ const DBT *src_key,
+ const DBT *src_val
+ )
+{
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ dest_key->size=src_key->size;
+ dest_key->data=src_key->data;
+ dest_key->flags = 0;
+ dest_val->size=src_val->size;
+ dest_val->data=src_val->data;
+ dest_val->flags = 0;
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->set_generate_row_callback_for_put(env,generate_row_for_put); CKERR(chk_r); }
+ { int chk_r = env->set_generate_row_callback_for_del(env,generate_row_for_del); CKERR(chk_r); }
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+ DB_LOADER* loader = NULL;
+ DBT key, val;
+ uint32_t mult_db_flags = 0;
+ uint32_t mult_dbt_flags = DB_DBT_REALLOC;
+ uint8_t key_data = 0;
+ uint8_t val_data = 0;
+
+
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+
+
+ dbt_init(&key,&key_data,sizeof(uint8_t));
+ dbt_init(&val,&val_data,sizeof(uint8_t));
+
+ val_data = 100;
+
+ //
+ // now create a loader
+ //
+ IN_TXN_COMMIT(env, NULL, txn_loader, 0, {
+ // create DB
+ { int chk_r = env->create_loader(
+ env,
+ txn_loader,
+ &loader,
+ db,
+ 1,
+ &db,
+ &mult_db_flags,
+ &mult_dbt_flags,
+ 0
+ ); CKERR(chk_r); }
+ { int chk_r = loader->put(loader, &key, &val); CKERR(chk_r); }
+ { int chk_r = loader->close(loader); CKERR(chk_r); }
+ });
+
+ val_data = 101;
+ IN_TXN_COMMIT(env, NULL, txn_update, 0, {
+ { int chk_r = db->update(db, txn_update, &key, &val, 0); CKERR(chk_r); }
+ });
+
+ key_data = 11;
+ val_data = 11;
+ IN_TXN_COMMIT(env, NULL, txn_update, 0, {
+ { int chk_r = db->update(db, txn_update, &key, &val, 0); CKERR(chk_r); }
+ });
+
+
+ DBC *cursor = NULL;
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db->cursor(db, txn_3, &cursor, 0); CKERR(chk_r); }
+ { int chk_r = cursor->c_get(cursor, &key, &val, DB_NEXT); CKERR(chk_r); }
+ assert(key.size == sizeof(uint8_t));
+ assert(val.size == sizeof(uint8_t));
+ assert(*(uint8_t *)(key.data) == 0);
+ assert(*(uint8_t *)(val.data) == 101);
+ { int chk_r = cursor->c_get(cursor, &key, &val, DB_NEXT); CKERR(chk_r); }
+ assert(key.size == sizeof(uint8_t));
+ assert(val.size == sizeof(uint8_t));
+ assert(*(uint8_t *)(key.data) == 11);
+ assert(*(uint8_t *)(val.data) == 11);
+ { int chk_r = cursor->c_close(cursor); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_restrict.cc b/storage/tokudb/PerconaFT/src/tests/test_restrict.cc
new file mode 100644
index 00000000000..9433cf0fd36
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_restrict.cc
@@ -0,0 +1,305 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+typedef struct {
+ int64_t left;
+ int64_t right;
+ int64_t last;
+ int found;
+ int direction;
+ int error_to_expect;
+} cont_extra;
+
+static int
+getf_continue(DBT const* key, DBT const* val, void* context) {
+ assert(key); // prob wrong?
+ assert(val); // prob wrong? make ifs if this fails
+ cont_extra *CAST_FROM_VOIDP(c, context);
+
+ assert(c->found >= 0);
+ assert(c->found < 3);
+ c->found++;
+ assert(key->size == 8);
+ assert(val->size == 8);
+ int64_t k = *(int64_t*)key->data;
+ int64_t v = *(int64_t*)val->data;
+ assert(k==v);
+ assert(k==c->last+c->direction);
+ c->last = k;
+ if (c->error_to_expect) {
+ assert(c->left <= k);
+ assert(k <= c->right);
+ }
+ if (c->found < 3) {
+ return TOKUDB_CURSOR_CONTINUE;
+ } else {
+ return 0;
+ }
+}
+
+static void
+test_restrict (int64_t n, int offset, int error_to_expect) {
+ assert(n > 30);
+ DB_TXN * const null_txn = 0;
+ int r;
+
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r=env->set_default_bt_compare(env, int64_dbt_cmp); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ r = db->set_flags(db, 0);
+ assert(r == 0);
+ r = db->open(db, null_txn, "restrict.db", NULL, DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+
+ int64_t keys[n];
+ int64_t i;
+ for (i=0; i<n; i++) {
+ keys[i] = i;
+ }
+
+ DBT key, val;
+ for (i=0; i<n; i++) {
+ r = db->put(db, null_txn, dbt_init(&key, &keys[i], sizeof keys[i]), dbt_init(&val, &i, sizeof i), 0);
+ assert(r == 0);
+ }
+
+ DBC* cursor;
+
+ r = db->cursor(db, NULL, &cursor, 0);
+ CKERR(r);
+
+ DBT dbt_left, dbt_right;
+ int64_t int_left, int_right;
+ int_left = n / 3 + offset;
+ int_right = 2 * n / 3 + offset;
+
+ dbt_init(&dbt_left, &keys[int_left], sizeof keys[int_left]);
+ dbt_init(&dbt_right, &keys[int_right], sizeof keys[int_right]);
+
+ r = cursor->c_set_bounds(
+ cursor,
+ &dbt_left,
+ &dbt_right,
+ true,
+ error_to_expect);
+ CKERR(r);
+
+
+ for (i=0; i<n; i++) {
+ r = cursor->c_get(cursor, dbt_init(&key, &keys[i], sizeof keys[i]), dbt_init(&val, NULL, 0), DB_SET);
+ if (i < int_left || i > int_right) {
+ CKERR2(r, error_to_expect);
+ } else {
+ CKERR(r);
+ assert(val.size == 8);
+ assert(*(int64_t*)val.data == i);
+ }
+ }
+ // Forwards
+
+ r = cursor->c_get(cursor, dbt_init(&key, &keys[int_left], sizeof keys[int_left]), dbt_init(&val, NULL, 0), DB_SET);
+ CKERR(r);
+ assert(val.size == 8);
+ assert(*(int64_t*)val.data == int_left);
+
+ for (i=int_left+1; i < n; i++) {
+ r = cursor->c_get(cursor, dbt_init(&key, NULL, 0), dbt_init(&val, NULL, 0), DB_NEXT);
+ if (i >= int_left && i <= int_right) {
+ CKERR(r);
+ assert(key.size == 8);
+ assert(*(int64_t*)key.data == i);
+ assert(val.size == 8);
+ assert(*(int64_t*)val.data == i);
+ } else {
+ CKERR2(r, error_to_expect);
+ break;
+ }
+ }
+
+ r = cursor->c_get(cursor, dbt_init(&key, &keys[int_right], sizeof keys[int_right]), dbt_init(&val, NULL, 0), DB_SET);
+ CKERR(r);
+ assert(val.size == 8);
+ assert(*(int64_t*)val.data == int_right);
+
+ for (i=int_right-1; i >= 0; i--) {
+ r = cursor->c_get(cursor, dbt_init(&key, NULL, 0), dbt_init(&val, NULL, 0), DB_PREV);
+ if (i >= int_left && i <= int_right) {
+ CKERR(r);
+ assert(key.size == 8);
+ assert(*(int64_t*)key.data == i);
+ assert(val.size == 8);
+ assert(*(int64_t*)val.data == i);
+ } else {
+ CKERR2(r, error_to_expect);
+ break;
+ }
+ }
+
+ // Forwards
+
+ r = cursor->c_get(cursor, dbt_init(&key, &keys[int_left], sizeof keys[int_left]), dbt_init(&val, NULL, 0), DB_SET);
+ CKERR(r);
+ assert(val.size == 8);
+ assert(*(int64_t*)val.data == int_left);
+
+ cont_extra c;
+ c.left = int_left;
+ c.right = int_right;
+ c.error_to_expect = error_to_expect;
+ c.direction = 1;
+ c.last = int_left;
+ for (i=int_left+1; i < n; i+=3) {
+ c.found = 0;
+
+ r = cursor->c_getf_next(cursor, 0, getf_continue, &c);
+ if (i >= int_left && i <= int_right) {
+ CKERR(r);
+ if (!error_to_expect) {
+ assert(c.found == 3);
+ assert(c.last == i+2);
+ } else if (i+2 >= int_left && i+2 <= int_right) {
+ assert(c.found == 3);
+ assert(c.last == i+2);
+ } else if (i+1 >= int_left && i+1 <= int_right) {
+ assert(c.found == 2);
+ assert(c.last == i+1);
+ r = cursor->c_get(cursor, dbt_init(&key, NULL, 0), dbt_init(&val, NULL, 0), DB_CURRENT);
+ CKERR2(r, error_to_expect);
+ break;
+ } else {
+ assert(c.found == 1);
+ assert(c.last == i);
+ r = cursor->c_get(cursor, dbt_init(&key, NULL, 0), dbt_init(&val, NULL, 0), DB_CURRENT);
+ CKERR2(r, error_to_expect);
+ break;
+ }
+ } else {
+ if (error_to_expect == 0) {
+ assert(c.found == 3);
+ assert(c.last == i+2);
+ } else {
+ assert(c.found == 0);
+ assert(c.last == i-1);
+ }
+ CKERR2(r, error_to_expect);
+ break;
+ }
+ }
+
+ r = cursor->c_get(cursor, dbt_init(&key, &keys[int_right], sizeof keys[int_right]), dbt_init(&val, NULL, 0), DB_SET);
+ CKERR(r);
+ assert(val.size == 8);
+ assert(*(int64_t*)val.data == int_right);
+
+ c.direction = -1;
+ c.last = int_right;
+ for (i=int_right-1; i >= 0; i -= 3) {
+ c.found = 0;
+
+ r = cursor->c_getf_prev(cursor, 0, getf_continue, &c);
+ if (i >= int_left && i <= int_right) {
+ CKERR(r);
+ if (!error_to_expect) {
+ assert(c.found == 3);
+ assert(c.last == i-2);
+ } else if (i-2 >= int_left && i-2 <= int_right) {
+ assert(c.found == 3);
+ assert(c.last == i-2);
+ } else if (i-1 >= int_left && i-1 <= int_right) {
+ assert(c.found == 2);
+ assert(c.last == i-1);
+ r = cursor->c_get(cursor, dbt_init(&key, NULL, 0), dbt_init(&val, NULL, 0), DB_CURRENT);
+ CKERR2(r, error_to_expect);
+ break;
+ } else {
+ assert(c.found == 1);
+ assert(c.last == i);
+ r = cursor->c_get(cursor, dbt_init(&key, NULL, 0), dbt_init(&val, NULL, 0), DB_CURRENT);
+ CKERR2(r, error_to_expect);
+ break;
+ }
+ } else {
+ if (error_to_expect == 0) {
+ assert(c.found == 3);
+ assert(c.last == i-2);
+ } else {
+ assert(c.found == 0);
+ assert(c.last == i+1);
+ }
+ CKERR2(r, error_to_expect);
+ break;
+ }
+ }
+
+ r = cursor->c_close(cursor); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ for (int i = 3*64; i < 3*1024; i *= 2) {
+ for (int offset = -2; offset <= 2; offset++) {
+ test_restrict(i, offset, DB_NOTFOUND);
+ test_restrict(i, offset, TOKUDB_OUT_OF_RANGE);
+ test_restrict(i, offset, 0);
+ }
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_reverse_compare_fun.cc b/storage/tokudb/PerconaFT/src/tests/test_reverse_compare_fun.cc
new file mode 100644
index 00000000000..5fc0ab476f6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_reverse_compare_fun.cc
@@ -0,0 +1,179 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+/* try a reverse compare function to verify that the database always uses the application's
+ compare function */
+
+
+#include <db.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+
+static int
+keycompare (const void *key1, unsigned int key1len, const void *key2, unsigned int key2len) {
+ if (key1len==key2len) {
+ return memcmp(key1,key2,key1len);
+ } else if (key1len<key2len) {
+ int r = memcmp(key1,key2,key1len);
+ if (r<=0) return -1; /* If the keys are the same up to 1's length, then return -1, since key1 is shorter than key2. */
+ else return 1;
+ } else {
+ return -keycompare(key2,key2len,key1,key1len);
+ }
+}
+
+static int
+reverse_compare (DB *db __attribute__((__unused__)), const DBT *a, const DBT*b) {
+ return -keycompare(a->data, a->size, b->data, b->size);
+}
+
+static void
+expect (DBC *cursor, int k, int v) {
+ DBT key, val;
+ int r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), DB_NEXT);
+ CKERR(r);
+ assert(key.size == sizeof k);
+ int kk;
+ memcpy(&kk, key.data, key.size);
+ assert(val.size == sizeof v);
+ int vv;
+ memcpy(&vv, val.data, val.size);
+ if (kk != k || vv != v) printf("expect key %u got %u - %u %u\n", (uint32_t)htonl(k), (uint32_t)htonl(kk), (uint32_t)htonl(v), (uint32_t)htonl(vv));
+ assert(kk == k);
+ assert(vv == v);
+
+ toku_free(key.data);
+ toku_free(val.data);
+}
+
+static void
+test_reverse_compare (int n) {
+ if (verbose) printf("test_reverse_compare:%d\n", n);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "reverse.compare.db";
+
+ int r;
+ int i;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->set_default_bt_compare(env, reverse_compare);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->set_pagesize(db, 4096);
+ CKERR(r);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666);
+ CKERR(r);
+
+ /* insert n unique keys {0, 1, n-1} */
+ for (i=0; i<n; i++) {
+ DBT key, val;
+ int k, v;
+ k = htonl(i);
+ dbt_init(&key, &k, sizeof k);
+ v = htonl(i);
+ dbt_init(&val, &v, sizeof v);
+ r = db->put(db, null_txn, &key, &val, 0);
+ CKERR(r);
+ }
+
+ /* reopen the database to force nonleaf buffering */
+ r = db->close(db, 0);
+ CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->set_pagesize(db, 4096);
+ CKERR(r);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, 0, 0666);
+ CKERR(r);
+
+ /* insert n unique keys {n, n+1, 2*n-1} */
+ for (i=n; i<2*n; i++) {
+ DBT key, val;
+ int k, v;
+ k = htonl(i);
+ dbt_init(&key, &k, sizeof k);
+ v = htonl(i);
+ dbt_init(&val, &v, sizeof v);
+ r = db->put(db, null_txn, &key, &val, 0);
+ CKERR(r);
+ }
+
+ /* verify the sort order with a cursor */
+ DBC *cursor;
+ r = db->cursor(db, null_txn, &cursor, 0);
+ CKERR(r);
+
+ //for (i=0; i<2*n; i++)
+ for (i=2*n-1; i>=0; i--)
+ expect(cursor, htonl(i), htonl(i));
+
+ r = cursor->c_close(cursor);
+ CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int i;
+
+ for (i = 1; i <= (1<<16); i *= 2) {
+ test_reverse_compare(i);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_set_func_malloc.cc b/storage/tokudb/PerconaFT/src/tests/test_set_func_malloc.cc
new file mode 100644
index 00000000000..e84437ce54d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_set_func_malloc.cc
@@ -0,0 +1,123 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#define DONT_DEPRECATE_MALLOC
+#include "test.h"
+
+/* Test to see if setting malloc works. */
+
+#include <memory.h>
+#include <db.h>
+
+static int malloc_counter=0;
+static int realloc_counter=0;
+static int free_counter=0;
+
+static void *
+bmalloc (size_t s)
+{
+ malloc_counter++;
+ return malloc(s);
+}
+
+static void
+bfree (void*p)
+{
+ free_counter++;
+ free(p);
+}
+
+static void*
+brealloc (void*p, size_t s)
+{
+ realloc_counter++;
+ return realloc(p,s);
+}
+
+static void
+test1 (void)
+{
+ DB_ENV *env=0;
+ int r;
+ r = db_env_create(&env, 0); assert(r==0);
+ r = env->close(env, 0); assert(r==0);
+ assert(malloc_counter==0);
+ assert(free_counter==0);
+ assert(realloc_counter==0);
+
+ db_env_set_func_malloc(bmalloc);
+ r = db_env_create(&env, 0); assert(r==0);
+ r = env->close(env, 0); assert(r==0);
+ assert(malloc_counter>0);
+ assert(free_counter==0);
+ assert(realloc_counter==0);
+
+ malloc_counter = realloc_counter = free_counter = 0;
+
+ db_env_set_func_free(bfree);
+ db_env_set_func_malloc(NULL);
+ r = db_env_create(&env, 0); assert(r==0);
+ r = env->close(env, 0); assert(r==0);
+ assert(malloc_counter==0);
+ assert(free_counter>=0);
+ assert(realloc_counter==0);
+
+ db_env_set_func_malloc(bmalloc);
+ db_env_set_func_realloc(brealloc);
+ db_env_set_func_free(bfree);
+
+ // toku_malloc isn't affected by calling the BDB set_fun_malloc calls.
+ malloc_counter = realloc_counter = free_counter = 0;
+
+ {
+ void *x = toku_malloc(5); assert(x); assert(malloc_counter==1 && free_counter==0 && realloc_counter==0);
+ x = toku_realloc(x, 6); assert(x); assert(malloc_counter==1 && free_counter==0 && realloc_counter==1);
+ toku_free(x); assert(malloc_counter==1 && free_counter==1 && realloc_counter==1);
+ }
+
+ db_env_set_func_malloc(NULL);
+ db_env_set_func_realloc(NULL);
+ db_env_set_func_free(NULL);
+}
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__)))
+{
+ test1();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_simple_read_txn.cc b/storage/tokudb/PerconaFT/src/tests/test_simple_read_txn.cc
new file mode 100644
index 00000000000..69e086785bf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_simple_read_txn.cc
@@ -0,0 +1,97 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+
+static void test_read_txn_creation(DB_ENV* env, uint32_t iso_flags) {
+ int r;
+ DB_TXN* parent_txn = NULL;
+ DB_TXN* child_txn = NULL;
+ r = env->txn_begin(env, 0, &parent_txn, iso_flags);
+ CKERR(r);
+ r = env->txn_begin(env, parent_txn, &child_txn, iso_flags | DB_TXN_READ_ONLY);
+ CKERR2(r, EINVAL);
+ r = env->txn_begin(env, parent_txn, &child_txn, iso_flags);
+ CKERR(r);
+ r = child_txn->commit(child_txn, 0);
+ CKERR(r);
+ r = parent_txn->commit(parent_txn, 0);
+ CKERR(r);
+
+ r = env->txn_begin(env, 0, &parent_txn, iso_flags | DB_TXN_READ_ONLY);
+ CKERR(r);
+ r = env->txn_begin(env, parent_txn, &child_txn, iso_flags | DB_TXN_READ_ONLY);
+ CKERR(r);
+ r = child_txn->commit(child_txn, 0);
+ CKERR(r);
+ r = env->txn_begin(env, parent_txn, &child_txn, iso_flags);
+ CKERR(r);
+ r = child_txn->commit(child_txn, 0);
+ CKERR(r);
+ r = parent_txn->commit(parent_txn, 0);
+ CKERR(r);
+
+}
+
+int test_main(int argc, char * const argv[])
+{
+ int r;
+ DB_ENV * env;
+ (void) argc;
+ (void) argv;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); { int chk_r = r; CKERR(chk_r); }
+
+ // set things up
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, 0755);
+ CKERR(r);
+
+ test_read_txn_creation(env, 0);
+ test_read_txn_creation(env, DB_TXN_SNAPSHOT);
+ test_read_txn_creation(env, DB_READ_COMMITTED);
+ test_read_txn_creation(env, DB_READ_UNCOMMITTED);
+
+ r = env->close(env, 0);
+ CKERR(r);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress0.cc b/storage/tokudb/PerconaFT/src/tests/test_stress0.cc
new file mode 100644
index 00000000000..aaafe284906
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress0.cc
@@ -0,0 +1,186 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a micro stress test that does multithreaded updates on a fixed size table.
+// There is also a thread that scans the table with bulk fetch, ensuring the sum is zero.
+//
+// This test is targetted at stressing the locktree, hence the small table and many update threads.
+//
+
+static int UU() lock_escalation_op(DB_TXN *UU(txn), ARG arg, void* operation_extra, void *UU(stats_extra)) {
+ invariant_null(operation_extra);
+ if (!arg->cli->nolocktree) {
+ toku_env_run_lock_escalation_for_test(arg->env);
+ }
+ return 0;
+}
+
+static int iterate_requests(DB *db, uint64_t txnid,
+ const DBT *left_key, const DBT *right_key,
+ uint64_t blocking_txnid,
+ uint64_t UU(start_time),
+ void *extra) {
+ invariant_null(extra);
+ invariant(db != nullptr);
+ invariant(txnid > 0);
+ invariant(left_key != nullptr);
+ invariant(right_key != nullptr);
+ invariant(blocking_txnid > 0);
+ invariant(txnid != blocking_txnid);
+ if (rand() % 5 == 0) {
+ usleep(100);
+ }
+ return 0;
+}
+
+static int UU() iterate_pending_lock_requests_op(DB_TXN *UU(txn), ARG arg, void *UU(operation_extra), void *UU(stats_extra)) {
+ DB_ENV *env = arg->env;
+ int r = env->iterate_pending_lock_requests(env, iterate_requests, nullptr);
+ invariant_zero(r);
+ return r;
+}
+
+static int iterate_txns(DB_TXN *txn,
+ iterate_row_locks_callback iterate_locks,
+ void *locks_extra, void *extra) {
+ uint64_t txnid = txn->id64(txn);
+ uint64_t client_id = txn->get_client_id(txn);
+ invariant_null(extra);
+ invariant(txnid > 0);
+ invariant(client_id == 0);
+ DB *db;
+ DBT left_key, right_key;
+ while (iterate_locks(&db, &left_key, &right_key, locks_extra) == 0) {
+ invariant_notnull(db);
+ invariant_notnull(left_key.data);
+ invariant(left_key.size > 0);
+ invariant_notnull(right_key.data);
+ invariant(right_key.size > 0);
+ if (rand() % 5 == 0) {
+ usleep(50);
+ }
+ memset(&left_key, 0, sizeof(DBT));
+ memset(&right_key, 0, sizeof(DBT));
+ }
+ return 0;
+}
+
+static int UU() iterate_live_transactions_op(DB_TXN *UU(txn), ARG arg, void *UU(operation_extra), void *UU(stats_extra)) {
+ DB_ENV *env = arg->env;
+ int r = env->iterate_live_transactions(env, iterate_txns, nullptr);
+ invariant_zero(r);
+ return r;
+}
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int non_update_threads = 4;
+ const int num_threads = non_update_threads + cli_args->num_update_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ struct scan_op_extra soe[1];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+
+ myargs[1].sleep_ms = 15L * 1000;
+ myargs[1].operation_extra = nullptr;
+ myargs[1].operation = lock_escalation_op;
+
+ myargs[2].sleep_ms = 1L * 1000;
+ myargs[2].operation_extra = nullptr;
+ myargs[2].operation = iterate_pending_lock_requests_op;
+
+ myargs[3].sleep_ms = 1L * 1000;
+ myargs[3].operation_extra = nullptr;
+ myargs[3].operation = iterate_live_transactions_op;
+
+ // make the threads that update the db
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ for (int i = non_update_threads; i < num_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ myargs[i].do_prepare = false;
+ // the first three threads will prelock ranges before
+ // doing sequential updates. the rest of the threads
+ // will take point write locks on update as usual.
+ // this ensures both ranges and points are stressed.
+ myargs[i].prelock_updates = i < 5 ? true : false;
+ }
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ // default args for first, then parse any overrides
+ args.num_update_threads = 8;
+ args.num_elements = 512;
+ args.txn_size = 16;
+ parse_stress_test_args(argc, argv, &args);
+
+ // we expect to get lock_notgranted op failures, and we
+ // don't want the overhead of fsync on small txns
+ args.crash_on_operation_failure = false;
+ args.env_args.sync_period = 100; // speed up the test by not fsyncing very often
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress1.cc b/storage/tokudb/PerconaFT/src/tests/test_stress1.cc
new file mode 100644
index 00000000000..a9bd860e5e8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress1.cc
@@ -0,0 +1,146 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - Grow the dictionary with insertions
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success. It also tests that snapshots
+// work correctly by verifying that table scans sum their vals to 0.
+//
+// This does NOT test:
+// - splits and merges
+// - multiple DBs
+//
+// Variables that are interesting to tweak and run:
+// - small cachetable
+// - number of elements
+//
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - some threads constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - some threads doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 4 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ struct scan_op_extra soe[4];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op;
+ myargs[1].txn_flags = DB_TXN_SNAPSHOT | DB_TXN_READ_ONLY;
+
+ // make the backward fast scanner
+ soe[2].fast = true;
+ soe[2].fwd = false;
+ soe[2].prefetch = false;
+ myargs[2].operation_extra = &soe[2];
+ myargs[2].operation = scan_op;
+ myargs[2].txn_flags = DB_TXN_SNAPSHOT | DB_TXN_READ_ONLY;
+
+ // make the backward slow scanner
+ soe[3].fast = false;
+ soe[3].fwd = false;
+ soe[3].prefetch = false;
+ myargs[3].operation_extra = &soe[3];
+ myargs[3].operation = scan_op;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 4; i < 4 + cli_args->num_update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ myargs[i].do_prepare = true;
+ }
+
+ // make the guy that does point queries
+ for (int i = 4 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].operation = ptquery_op;
+ myargs[i].do_prepare = true;
+ }
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ parse_stress_test_args(argc, argv, &args);
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress2.cc b/storage/tokudb/PerconaFT/src/tests/test_stress2.cc
new file mode 100644
index 00000000000..5ea57625127
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress2.cc
@@ -0,0 +1,140 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - update existing values in the dictionary with db->put(DB_YESOVERWRITE)
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success.
+//
+// This test differs from stress1 in that it grows the database through
+// update operations.
+//
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - one thread constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - one thread doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 4 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ struct scan_op_extra soe[4];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op;
+ myargs[1].txn_flags = DB_TXN_SNAPSHOT | DB_TXN_READ_ONLY;
+
+ // make the backward fast scanner
+ soe[2].fast = true;
+ soe[2].fwd = false;
+ soe[2].prefetch = false;
+ myargs[2].operation_extra = &soe[2];
+ myargs[2].operation = scan_op;
+ myargs[2].txn_flags = DB_TXN_SNAPSHOT | DB_TXN_READ_ONLY;
+
+ // make the backward slow scanner
+ soe[3].fast = false;
+ soe[3].fwd = false;
+ soe[3].prefetch = false;
+ myargs[3].operation_extra = &soe[3];
+ myargs[3].operation = scan_op;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 4; i < 4 + cli_args->num_update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].bounded_element_range = false;
+ myargs[i].operation = update_op;
+ }
+
+ // make the guy that does point queries
+ for (int i = 4 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].bounded_element_range = false;
+ myargs[i].operation = ptquery_op_no_check;
+ }
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ parse_stress_test_args(argc, argv, &args);
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress3.cc b/storage/tokudb/PerconaFT/src/tests/test_stress3.cc
new file mode 100644
index 00000000000..b8ca68ab9d9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress3.cc
@@ -0,0 +1,143 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - update existing values in the dictionary with db->put(DB_YESOVERWRITE)
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success.
+//
+// This test differs from stress2 in that it sends periodic update broadcasts.
+//
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - one thread constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - one thread doing random point queries
+ //
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 5 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+
+ struct scan_op_extra soe[4];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op;
+ myargs[1].txn_flags = DB_TXN_SNAPSHOT | DB_TXN_READ_ONLY;
+
+ // make the backward fast scanner
+ soe[2].fast = true;
+ soe[2].fwd = false;
+ soe[2].prefetch = false;
+ myargs[2].operation_extra = &soe[2];
+ myargs[2].operation = scan_op;
+ myargs[2].txn_flags = DB_TXN_SNAPSHOT | DB_TXN_READ_ONLY;
+
+ // make the backward slow scanner
+ soe[3].fast = false;
+ soe[3].fwd = false;
+ soe[3].prefetch = false;
+ myargs[3].operation_extra = &soe[3];
+ myargs[3].operation = scan_op;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 4; i < 4 + cli_args->num_update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].lock_type = STRESS_LOCK_SHARED;
+ myargs[i].operation = update_op;
+ }
+
+ // make the guy that sends update broadcasts
+ myargs[4 + cli_args->num_update_threads].lock_type = STRESS_LOCK_EXCL;
+ myargs[4 + cli_args->num_update_threads].sleep_ms = cli_args->update_broadcast_period_ms;
+ myargs[4 + cli_args->num_update_threads].operation = update_broadcast_op;
+
+ // make the guys that do point queries
+ for (int i = 5 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].operation = ptquery_op;
+ }
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ parse_stress_test_args(argc, argv, &args);
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress4.cc b/storage/tokudb/PerconaFT/src/tests/test_stress4.cc
new file mode 100644
index 00000000000..1405bfc1c02
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress4.cc
@@ -0,0 +1,139 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - update existing values in the dictionary with db->put(DB_YESOVERWRITE)
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success.
+//
+// This test differs from stress2 in that it verifies the last value on an update.
+//
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ int n = cli_args->num_elements;
+ //
+ // the threads that we want:
+ // - one thread constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - one thread doing random point queries
+ //
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 4 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+
+ struct scan_op_extra soe[4];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op_no_check;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op_no_check;
+
+ // make the backward fast scanner
+ soe[2].fast = true;
+ soe[2].fwd = false;
+ soe[2].prefetch = false;
+ myargs[2].operation_extra = &soe[2];
+ myargs[2].operation = scan_op_no_check;
+
+ // make the backward slow scanner
+ soe[3].fast = false;
+ soe[3].fwd = false;
+ soe[3].prefetch = false;
+ myargs[3].operation_extra = &soe[3];
+ myargs[3].operation = scan_op_no_check;
+
+ // make the guy that updates the db
+ invariant(cli_args->num_update_threads == 1);
+ int *XCALLOC_N(n, update_history_buffer);
+ struct update_op_args uoe = get_update_op_args(cli_args, update_history_buffer);
+ myargs[4].operation = update_with_history_op;
+ myargs[4].operation_extra = &uoe;
+
+ // make the guys that do point queries
+ for (int i = 5; i < num_threads; i++) {
+ myargs[i].operation = ptquery_op;
+ }
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+
+ toku_free(update_history_buffer);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ parse_stress_test_args(argc, argv, &args);
+ args.num_update_threads = 1; // if we had more than 1 update thread, we would need locking for the update_history_buffer.
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress5.cc b/storage/tokudb/PerconaFT/src/tests/test_stress5.cc
new file mode 100644
index 00000000000..89365d19b47
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress5.cc
@@ -0,0 +1,113 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // do insertions and queries with a loader lying around doing stuff
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 5 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ struct scan_op_extra soe[2];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+ myargs[0].txn_flags = DB_TXN_SNAPSHOT | DB_TXN_READ_ONLY;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op;
+
+ // make the guy that updates the db
+ struct loader_op_extra loe;
+ loe.soe = soe[0];
+ loe.num_dbs = 3;
+ myargs[2].operation_extra = &loe;
+ myargs[2].operation = loader_op;
+ myargs[3].operation = keyrange_op;
+ myargs[4].operation = get_key_after_bytes_op;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 5; i < 5 + cli_args->num_update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ }
+
+ // make the guy that does point queries
+ for (int i = 5 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].operation = ptquery_op;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ // let's make default checkpointing period really slow
+ args.env_args.checkpointing_period = 1;
+ parse_stress_test_args(argc, argv, &args);
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress6.cc b/storage/tokudb/PerconaFT/src/tests/test_stress6.cc
new file mode 100644
index 00000000000..3082bdaba9e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress6.cc
@@ -0,0 +1,171 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+//
+// This test is a form of stress that does operations on a single dictionary:
+// We create a dictionary bigger than the cachetable (around 4x greater).
+// Then, we spawn a bunch of pthreads that do the following:
+// - scan dictionary forward with bulk fetch
+// - scan dictionary forward slowly
+// - scan dictionary backward with bulk fetch
+// - scan dictionary backward slowly
+// - update existing values in the dictionary with db->put(DB_YESOVERWRITE)
+// - do random point queries into the dictionary
+// With the small cachetable, this should produce quite a bit of churn in reading in and evicting nodes.
+// If the test runs to completion without crashing, we consider it a success.
+//
+// This test differs from stress1 in that it grows the database through
+// update operations.
+//
+
+static int remove_and_recreate_me(DB_TXN *UU(txn), ARG arg, void* UU(operation_extra), void *UU(stats_extra)) {
+ int r;
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+ r = (db)->close(db, 0); CKERR(r);
+
+ char name[30];
+ ZERO_ARRAY(name);
+ get_ith_table_name(name, sizeof(name), db_index);
+
+ r = arg->env->dbremove(arg->env, null_txn, name, nullptr, 0);
+ CKERR(r);
+
+ r = db_create(&(arg->dbp[db_index]), arg->env, 0);
+ assert(r == 0);
+ // TODO: Need to call before_db_open_hook() and after_db_open_hook()
+ r = arg->dbp[db_index]->open(arg->dbp[db_index], null_txn, name, nullptr, DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+ return 0;
+}
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // the threads that we want:
+ // - one thread constantly updating random values
+ // - one thread doing table scan with bulk fetch
+ // - one thread doing table scan without bulk fetch
+ // - one thread doing random point queries
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 5 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+
+ struct scan_op_extra soe[4];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].lock_type = STRESS_LOCK_SHARED;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].lock_type = STRESS_LOCK_SHARED;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op;
+
+ // make the backward fast scanner
+ soe[2].fast = true;
+ soe[2].fwd = false;
+ soe[2].prefetch = false;
+ myargs[2].lock_type = STRESS_LOCK_SHARED;
+ myargs[2].operation_extra = &soe[2];
+ myargs[2].operation = scan_op;
+
+ // make the backward slow scanner
+ soe[3].fast = false;
+ soe[3].fwd = false;
+ soe[3].prefetch = false;
+ myargs[3].lock_type = STRESS_LOCK_SHARED;
+ myargs[3].operation_extra = &soe[3];
+ myargs[3].operation = scan_op;
+
+ // make the guy that removes and recreates the db
+ myargs[4].lock_type = STRESS_LOCK_EXCL;
+ myargs[4].sleep_ms = 2000; // maybe make this a runtime param at some point
+ myargs[4].operation = remove_and_recreate_me;
+
+ // make the guy that updates the db
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ for (int i = 5; i < 5 + cli_args->num_update_threads; ++i) {
+ myargs[i].bounded_element_range = false;
+ myargs[i].lock_type = STRESS_LOCK_SHARED;
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ }
+
+ // make the guy that does point queries
+ for (int i = 5 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].lock_type = STRESS_LOCK_SHARED;
+ myargs[i].bounded_element_range = false;
+ myargs[i].operation = ptquery_op_no_check;
+ }
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ parse_stress_test_args(argc, argv, &args);
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress7.cc b/storage/tokudb/PerconaFT/src/tests/test_stress7.cc
new file mode 100644
index 00000000000..f42676886fe
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress7.cc
@@ -0,0 +1,109 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // do insertions and queries with a loader lying around doing stuff
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 5 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ struct scan_op_extra soe[2];
+
+ // make the forward fast scanner
+ soe[0].fast = true;
+ soe[0].fwd = true;
+ soe[0].prefetch = false;
+ myargs[0].operation_extra = &soe[0];
+ myargs[0].operation = scan_op;
+
+ // make the forward slow scanner
+ soe[1].fast = false;
+ soe[1].fwd = true;
+ soe[1].prefetch = false;
+ myargs[1].operation_extra = &soe[1];
+ myargs[1].operation = scan_op;
+
+ // make the guys that run hot optimize, keyrange, and frag stats in the background
+ myargs[2].operation = hot_op;
+ myargs[3].operation = keyrange_op;
+ myargs[4].operation = frag_op;
+ myargs[4].sleep_ms = 100;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ // make the guy that updates the db
+ for (int i = 5; i < 5 + cli_args->num_update_threads; ++i) {
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ }
+
+ // make the guy that does point queries
+ for (int i = 5 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].operation = ptquery_op;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ // let's make default checkpointing period really slow
+ args.env_args.checkpointing_period = 1;
+ parse_stress_test_args(argc, argv, &args);
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress_hot_indexing.cc b/storage/tokudb/PerconaFT/src/tests/test_stress_hot_indexing.cc
new file mode 100644
index 00000000000..e0bf0d2ec6f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress_hot_indexing.cc
@@ -0,0 +1,334 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+DB* hot_db;
+toku_mutex_t fops_lock;
+toku_mutex_t hi_lock;
+uint32_t gid_count;
+uint8_t hi_gid[DB_GID_SIZE];
+
+
+static int
+hi_put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ lazy_assert(src_db != NULL && dest_db != NULL);
+
+ if (dest_key->data) {
+ toku_free(dest_key->data);
+ dest_key->data = NULL;
+ }
+ if (dest_val->data) {
+ toku_free(dest_val->data);
+ dest_val->data = NULL;
+ }
+ dest_key->data = toku_xmemdup(src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ dest_val->data = toku_xmemdup(src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+
+ return 0;
+}
+
+static int
+hi_del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, const DBT *src_key, const DBT* UU(src_data)) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ lazy_assert(src_db != NULL && dest_db != NULL);
+ if (dest_key->data) {
+ toku_free(dest_key->data);
+ dest_key->data = NULL;
+ }
+ dest_key->data = toku_xmemdup(src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+
+ return 0;
+}
+
+
+static int hi_inserts(DB_TXN* UU(txn), ARG arg, void* UU(operation_extra), void *stats_extra) {
+ int r;
+ DB_TXN* hi_txn = NULL;
+ toku_mutex_lock(&fops_lock);
+ DB_ENV* env = arg->env;
+ DB* db = arg->dbp[0];
+ uint32_t flags[2];
+ flags[0] = 0;
+ flags[1] = 0;
+ DBT_ARRAY dest_keys[2];
+ DBT_ARRAY dest_vals[2];
+ for (int j = 0; j < 2; j++) {
+ toku_dbt_array_init(&dest_keys[j], 1);
+ toku_dbt_array_init(&dest_vals[j], 1);
+ }
+
+ DBT key, val;
+ uint8_t keybuf[arg->cli->key_size];
+ uint8_t valbuf[arg->cli->val_size];
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, valbuf, sizeof valbuf);
+
+ int i;
+ r = env->txn_begin(env, NULL, &hi_txn, 0);
+ CKERR(r);
+ for (i = 0; i < 1000; i++) {
+ DB* dbs[2];
+ toku_mutex_lock(&hi_lock);
+ dbs[0] = db;
+ dbs[1] = hot_db;
+ int num_dbs = hot_db ? 2 : 1;
+ // do a random insertion. the assertion comes from the fact
+ // that the code used to generate a random key and mod it
+ // by the table size manually. fill_key_buf_random will
+ // do this iff arg->bounded_element_range is true.
+ invariant(arg->bounded_element_range);
+ fill_key_buf_random(arg->random_data, keybuf, arg);
+ fill_val_buf_random(arg->random_data, valbuf, arg->cli);
+ r = env->put_multiple(
+ env,
+ db,
+ hi_txn,
+ &key,
+ &val,
+ num_dbs,
+ dbs,
+ dest_keys,
+ dest_vals,
+ flags
+ );
+ toku_mutex_unlock(&hi_lock);
+ if (r != 0) {
+ goto cleanup;
+ }
+ }
+cleanup:
+ for (int j = 0; j < 2; j++) {
+ toku_dbt_array_destroy(&dest_keys[j]);
+ toku_dbt_array_destroy(&dest_vals[j]);
+ }
+ increment_counter(stats_extra, PUTS, i);
+ gid_count++;
+ uint32_t *hi_gid_count_p = cast_to_typeof(hi_gid_count_p) hi_gid; // make gcc --happy about -Wstrict-aliasing
+ *hi_gid_count_p = gid_count;
+ int rr = hi_txn->prepare(hi_txn, hi_gid, 0);
+ CKERR(rr);
+ if (r || (random() % 2)) {
+ rr = hi_txn->abort(hi_txn);
+ CKERR(rr);
+ }
+ else {
+ rr = hi_txn->commit(hi_txn, 0);
+ CKERR(rr);
+ }
+ toku_mutex_unlock(&fops_lock);
+ return r;
+}
+
+static int indexer_maybe_quit_poll(void *UU(poll_extra), float UU(progress)) {
+ return run_test ? 0 : TOKUDB_CANCELED;
+}
+
+static int hi_create_index(DB_TXN* UU(txn), ARG arg, void* UU(operation_extra), void* UU(stats_extra)) {
+ int r;
+ DB_TXN* hi_txn = NULL;
+ DB_ENV* env = arg->env;
+ DB* db = arg->dbp[0];
+ DB_INDEXER* indexer = NULL;
+ r = env->txn_begin(env, NULL, &hi_txn, 0);
+ CKERR(r);
+ toku_mutex_lock(&hi_lock);
+ assert(hot_db == NULL);
+ db_create(&hot_db, env, 0);
+ CKERR(r);
+ r = hot_db->set_flags(hot_db, 0);
+ CKERR(r);
+ r = hot_db->set_pagesize(hot_db, arg->cli->env_args.node_size);
+ CKERR(r);
+ r = hot_db->set_readpagesize(hot_db, arg->cli->env_args.basement_node_size);
+ CKERR(r);
+ r = hot_db->open(hot_db, NULL, "hotindex_db", NULL, DB_BTREE, DB_CREATE | DB_IS_HOT_INDEX, 0666);
+ CKERR(r);
+ uint32_t db_flags = 0;
+ uint32_t indexer_flags = 0;
+
+ r = env->create_indexer(
+ env,
+ hi_txn,
+ &indexer,
+ arg->dbp[0],
+ 1,
+ &hot_db,
+ &db_flags,
+ indexer_flags
+ );
+ CKERR(r);
+ toku_mutex_unlock(&hi_lock);
+
+ r = indexer->set_poll_function(indexer, indexer_maybe_quit_poll, nullptr);
+ CKERR(r);
+
+ r = indexer->build(indexer);
+ CKERR2s(r, 0, TOKUDB_CANCELED);
+
+ toku_mutex_lock(&hi_lock);
+ r = indexer->close(indexer);
+ CKERR(r);
+ toku_mutex_unlock(&hi_lock);
+
+ r = hi_txn->commit(hi_txn, 0);
+ hi_txn = NULL;
+ CKERR(r);
+
+ // now do a scan to make sure hot index is good
+ DB_TXN* scan_txn = NULL;
+ DBC* main_cursor = NULL;
+ DBC* hi_cursor = NULL;
+ r = env->txn_begin(env, NULL, &scan_txn, DB_TXN_SNAPSHOT);
+ CKERR(r);
+ r = db->cursor(db, scan_txn, &main_cursor, 0);
+ CKERR(r);
+ r = db->cursor(hot_db, scan_txn, &hi_cursor, 0);
+ CKERR(r);
+ DBT key1, key2, val1, val2;
+ memset(&key1, 0, sizeof key1);
+ memset(&val1, 0, sizeof val1);
+ memset(&key2, 0, sizeof key2);
+ memset(&val2, 0, sizeof val2);
+ uint64_t count = 0;
+ while(r != DB_NOTFOUND) {
+ if (count++ % 256 == 0 && !run_test) {
+ r = TOKUDB_CANCELED;
+ break;
+ }
+ // get next from both cursors and assert they are equal
+ int r1 = main_cursor->c_get(
+ main_cursor,
+ &key1,
+ &val1,
+ DB_NEXT
+ );
+ int r2 = hi_cursor->c_get(
+ hi_cursor,
+ &key2,
+ &val2,
+ DB_NEXT
+ );
+ assert(r1 == r2);
+ r = r1;
+ if (r != DB_NOTFOUND) {
+ assert(key1.size == key2.size);
+ assert(val1.size == val2.size);
+ assert(memcmp(key1.data, key2.data, key1.size) == 0);
+ assert(memcmp(val1.data, val2.data, val1.size) == 0);
+ }
+ }
+ CKERR2s(r, DB_NOTFOUND, TOKUDB_CANCELED);
+ r = main_cursor->c_close(main_cursor);
+ r = hi_cursor->c_close(hi_cursor);
+ CKERR(r);
+ r = scan_txn->commit(scan_txn, 0);
+ CKERR(r);
+
+ // grab lock and close hot_db, set it to NULL
+ toku_mutex_lock(&hi_lock);
+ r = hot_db->close(hot_db, 0);
+ CKERR(r);
+ hot_db = NULL;
+ toku_mutex_unlock(&hi_lock);
+
+ toku_mutex_lock(&fops_lock);
+ r = env->dbremove(env, NULL, "hotindex_db", NULL, 0);
+ toku_mutex_unlock(&fops_lock);
+ CKERR(r);
+ return 0;
+}
+
+//
+// purpose of this stress test is to do a bunch of splitting and merging
+// and run db->verify periodically to make sure the db is in a
+// a good state
+//
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 2;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ myargs[0].operation = hi_inserts;
+ myargs[1].operation = hi_create_index;
+
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ gid_count = 0;
+ memset(hi_gid, 0, sizeof(hi_gid));
+ toku_mutex_init(&hi_lock, NULL);
+ toku_mutex_init(&fops_lock, NULL);
+ hot_db = NULL;
+ struct cli_args args = get_default_args();
+ // let's make default checkpointing period really slow
+ args.num_ptquery_threads = 0;
+ parse_stress_test_args(argc, argv, &args);
+ args.num_DBs = 1;
+ args.crash_on_operation_failure = false;
+ args.env_args.generate_del_callback = hi_del_callback;
+ args.env_args.generate_put_callback = hi_put_callback;
+ stress_test_main(&args);
+ toku_mutex_destroy(&hi_lock);
+ toku_mutex_destroy(&fops_lock);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress_openclose.cc b/storage/tokudb/PerconaFT/src/tests/test_stress_openclose.cc
new file mode 100644
index 00000000000..b4d56794fbd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress_openclose.cc
@@ -0,0 +1,56 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "stress_openclose.h"
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ parse_stress_test_args(argc, argv, &args);
+ // checkpointing is a part of the ref count, so do it often
+ args.env_args.checkpointing_period = 5;
+ // very small dbs, so verification scans are short and sweet
+ args.num_elements = 1000;
+ // it's okay for update to get DB_LOCK_NOTGRANTED, etc.
+ args.crash_on_operation_failure = false;
+
+ // just run the stress test, no crashing and recovery test
+ stress_openclose_crash_at_end = false;
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_stress_with_verify.cc b/storage/tokudb/PerconaFT/src/tests/test_stress_with_verify.cc
new file mode 100644
index 00000000000..cdd98739e4f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_stress_with_verify.cc
@@ -0,0 +1,110 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <toku_pthread.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+#include "threaded_stress_test_helpers.h"
+
+
+//
+// purpose of this stress test is to do a bunch of splitting and merging
+// and run db->verify periodically to make sure the db is in a
+// a good state
+//
+
+static void
+stress_table(DB_ENV *env, DB **dbp, struct cli_args *cli_args) {
+ //
+ // do insertions and queries with a loader lying around doing stuff
+ //
+
+ if (verbose) printf("starting creation of pthreads\n");
+ const int num_threads = 4 + cli_args->num_update_threads + cli_args->num_ptquery_threads;
+ struct arg myargs[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ arg_init(&myargs[i], dbp, env, cli_args);
+ }
+ // make the forward fast scanner
+ struct scan_op_extra soe;
+ soe.fast = true;
+ soe.fwd = true;
+ soe.prefetch = false;
+ myargs[0].operation_extra = &soe;
+ myargs[0].lock_type = STRESS_LOCK_SHARED;
+ myargs[0].operation = scan_op;
+
+ // make the backward slow scanner
+ myargs[1].lock_type = STRESS_LOCK_EXCL;
+ myargs[1].sleep_ms = 3000; // maybe make this a runtime param at some point
+ myargs[1].operation = verify_op;
+
+ struct update_op_args uoe = get_update_op_args(cli_args, NULL);
+ for (int i = 2; i < 2 + cli_args->num_update_threads; ++i) {
+ myargs[i].lock_type = STRESS_LOCK_SHARED;
+ myargs[i].operation_extra = &uoe;
+ myargs[i].operation = update_op;
+ }
+
+ // make the guy that does point queries
+ for (int i = 2 + cli_args->num_update_threads; i < num_threads; i++) {
+ myargs[i].lock_type = STRESS_LOCK_SHARED;
+ myargs[i].operation = ptquery_op;
+ }
+ run_workers(myargs, num_threads, cli_args->num_seconds, false, cli_args);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ struct cli_args args = get_default_args();
+ // let's make default checkpointing period really slow
+ args.env_args.checkpointing_period = 1;
+ args.num_elements= 2000; // make default of small num elements to
+ args.num_ptquery_threads = 0;
+ parse_stress_test_args(argc, argv, &args);
+ stress_test_main(&args);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_thread_flags.cc b/storage/tokudb/PerconaFT/src/tests/test_thread_flags.cc
new file mode 100644
index 00000000000..a813d94b2f0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_thread_flags.cc
@@ -0,0 +1,132 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <memory.h>
+
+const char *dbfile = "test.db";
+const char *dbname = 0;
+
+static int
+db_put (DB *db, int k, int v) {
+ DBT key, val;
+ int r = db->put(db, 0, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ return r;
+}
+
+static int
+db_get (DB *db, int k, int expectv, int val_flags) {
+ int v;
+ DBT key, val;
+ memset(&val, 0, sizeof val); val.flags = val_flags;
+ if (val.flags == DB_DBT_USERMEM) {
+ val.ulen = sizeof v; val.data = &v;
+ }
+ int r = db->get(db, 0, dbt_init(&key, &k, sizeof k), &val, 0);
+ if (r == 0) {
+ assert(val.size == sizeof v);
+ if ((val.flags & DB_DBT_USERMEM) == 0) memcpy(&v, val.data, val.size);
+ assert(v == expectv);
+ } else {
+ if (verbose) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ }
+ if (val.flags & (DB_DBT_MALLOC|DB_DBT_REALLOC))
+ toku_free(val.data);
+ return r;
+}
+
+static void
+test_db_create (void) {
+ int r;
+
+ unlink(dbfile);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, 0, dbfile, dbname, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+ r = db_put(db, htonl(1), 1); assert(r == 0);
+ r = db_get(db, htonl(1), 1, 0); assert(r == 0);
+ r = db_get(db, htonl(1), 1, DB_DBT_USERMEM); assert(r == 0);
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+static void
+test_db_thread (void) {
+ int r;
+
+ unlink(dbfile);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL+DB_THREAD, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,0); // Turn off those annoying errors
+ r = db->open(db, 0, dbfile, dbname, DB_BTREE, DB_CREATE + DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+ r = db_put(db, htonl(1), 1); assert(r == 0);
+ r = db_get(db, htonl(1), 1, 0); assert(r == EINVAL);
+ r = db_get(db, htonl(1), 1, DB_DBT_MALLOC); assert(r == 0);
+ r = db_get(db, htonl(1), 1, DB_DBT_REALLOC); assert(r == 0);
+ r = db_get(db, htonl(1), 1, DB_DBT_USERMEM); assert(r == 0);
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ test_db_create();
+ test_db_thread();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_thread_insert.cc b/storage/tokudb/PerconaFT/src/tests/test_thread_insert.cc
new file mode 100644
index 00000000000..0d5245e8f80
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_thread_insert.cc
@@ -0,0 +1,171 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <toku_pthread.h>
+
+static inline unsigned int getmyid(void) {
+ return toku_os_gettid();
+}
+
+typedef unsigned int my_t;
+
+struct db_inserter {
+ toku_pthread_t tid;
+ DB *db;
+ my_t startno, endno;
+ int do_exit;
+};
+
+static int
+db_put (DB *db, my_t k, my_t v) {
+ DBT key, val;
+ int r = db->put(db, 0, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ return r;
+}
+
+static void *
+do_inserts (void *arg) {
+ struct db_inserter *mywork = (struct db_inserter *) arg;
+ if (verbose) {
+ toku_pthread_t self = toku_pthread_self();
+ printf("%lu:%u:do_inserts:start:%u-%u\n", *(unsigned long*)&self, getmyid(), mywork->startno, mywork->endno);
+ }
+ my_t i;
+ for (i=mywork->startno; i < mywork->endno; i++) {
+ int r = db_put(mywork->db, htonl(i), i); assert(r == 0);
+ }
+
+ if (verbose) {
+ toku_pthread_t self = toku_pthread_self();
+ printf("%lu:%u:do_inserts:end\n", *(unsigned long*)&self, getmyid());
+ }
+ // Don't call toku_pthread_exit(), since it has a memory leak.
+ // if (mywork->do_exit) toku_pthread_exit(arg);
+ return 0;
+}
+
+static int
+usage (void) {
+ fprintf(stderr, "test [-n NTUPLES] [-p NTHREADS]\n");
+ fprintf(stderr, "default NTUPLES=1000000\n");
+ fprintf(stderr, "default NTHREADS=2\n");
+ return 1;
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ const char *dbfile = "test.db";
+ const char *dbname = "main";
+ int nthreads = 2;
+ my_t n = 1000000;
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ int i;
+ for (i=1; i<argc; i++) {
+ const char *arg = argv[i];
+ if (0 == strcmp(arg, "-h") || 0 == strcmp(arg, "--help")) {
+ return usage();
+ }
+ if (0 == strcmp(arg, "-v") || 0 == strcmp(arg, "--verbose")) {
+ verbose = 1;
+ continue;
+ }
+ if (0 == strcmp(arg, "-p")) {
+ if (i+1 >= argc) return usage();
+ nthreads = atoi(argv[++i]);
+ continue;
+ }
+ if (0 == strcmp(arg, "-n")) {
+ if (i+1 >= argc) return usage();
+ n = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ DB_ENV *env;
+
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->set_cachesize(env, 0, 128000000, 1); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE + DB_THREAD + DB_PRIVATE + DB_INIT_MPOOL + DB_INIT_LOCK, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+
+ DB *db;
+
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, 0, dbfile, dbname, DB_BTREE, DB_CREATE + DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+
+ struct db_inserter work[nthreads];
+
+ for (i=0; i<nthreads; i++) {
+ work[i].db = db;
+ work[i].startno = i*(n/nthreads);
+ work[i].endno = work[i].startno + (n/nthreads);
+ work[i].do_exit =1 ;
+ if (i+1 == nthreads)
+ work[i].endno = n;
+ }
+
+ if (verbose) printf("pid:%d\n", toku_os_getpid());
+
+ for (i=1; i<nthreads; i++) {
+ r = toku_pthread_create(&work[i].tid, 0, do_inserts, &work[i]); assert(r == 0);
+ }
+
+ work[0].do_exit = 0;
+ do_inserts(&work[0]);
+
+ for (i=1; i<nthreads; i++) {
+ void *ret;
+ r = toku_pthread_join(work[i].tid, &ret); assert(r == 0);
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt.cc b/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt.cc
new file mode 100644
index 00000000000..8dadb0ee3dd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt.cc
@@ -0,0 +1,132 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ DB* db = NULL;
+
+ DBT change_descriptor;
+ memset(&change_descriptor, 0, sizeof(change_descriptor));
+ change_descriptor.size = sizeof(eight_byte_desc);
+ change_descriptor.data = &eight_byte_desc;
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ IN_TXN_COMMIT(env, NULL, txn_change, 0, {
+ { int chk_r = db->change_descriptor(db, txn_change, &change_descriptor, 0); CKERR(chk_r); }
+ assert_desc_eight(db);
+ });
+ assert_desc_eight(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+
+ DBT orig_desc;
+ memset(&orig_desc, 0, sizeof(orig_desc));
+ orig_desc.size = sizeof(four_byte_desc);
+ orig_desc.data = &four_byte_desc;
+ // verify we can only set a descriptor with version 1
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ assert(db->descriptor == NULL);
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn_create, &orig_desc, 0); CKERR(chk_r); }
+ assert_desc_four(db);
+ });
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_eight(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ db = NULL;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt2.cc b/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt2.cc
new file mode 100644
index 00000000000..612c352d44c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt2.cc
@@ -0,0 +1,132 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ DB* db = NULL;
+
+ DBT change_descriptor;
+ memset(&change_descriptor, 0, sizeof(change_descriptor));
+ change_descriptor.size = sizeof(eight_byte_desc);
+ change_descriptor.data = &eight_byte_desc;
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ IN_TXN_ABORT(env, NULL, txn_change, 0, {
+ { int chk_r = db->change_descriptor(db, txn_change, &change_descriptor, 0); CKERR(chk_r); }
+ assert_desc_eight(db);
+ });
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+
+ DBT orig_desc;
+ memset(&orig_desc, 0, sizeof(orig_desc));
+ orig_desc.size = sizeof(four_byte_desc);
+ orig_desc.data = &four_byte_desc;
+ // verify we can only set a descriptor with version 1
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ assert(db->descriptor == NULL);
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn_create, &orig_desc, 0); CKERR(chk_r); }
+ assert_desc_four(db);
+ });
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ db = NULL;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt3.cc b/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt3.cc
new file mode 100644
index 00000000000..43569b2a3ba
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt3.cc
@@ -0,0 +1,132 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ DB* db = NULL;
+
+ DBT change_descriptor;
+ memset(&change_descriptor, 0, sizeof(change_descriptor));
+ change_descriptor.size = sizeof(eight_byte_desc);
+ change_descriptor.data = &eight_byte_desc;
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ IN_TXN_COMMIT(env, NULL, txn_change, 0, {
+ { int chk_r = db->change_descriptor(db, txn_change, &change_descriptor, 0); CKERR(chk_r); }
+ assert_desc_eight(db);
+ });
+ assert_desc_eight(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback2(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+
+ DBT orig_desc;
+ memset(&orig_desc, 0, sizeof(orig_desc));
+ orig_desc.size = sizeof(four_byte_desc);
+ orig_desc.data = &four_byte_desc;
+ // verify we can only set a descriptor with version 1
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ assert(db->descriptor == NULL);
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn_create, &orig_desc, 0); CKERR(chk_r); }
+ assert_desc_four(db);
+ });
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_eight(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ db = NULL;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt4.cc b/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt4.cc
new file mode 100644
index 00000000000..612c352d44c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_trans_desc_during_chkpt4.cc
@@ -0,0 +1,132 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void checkpoint_callback_1(void * extra) {
+ assert(extra == NULL);
+ DB* db = NULL;
+
+ DBT change_descriptor;
+ memset(&change_descriptor, 0, sizeof(change_descriptor));
+ change_descriptor.size = sizeof(eight_byte_desc);
+ change_descriptor.data = &eight_byte_desc;
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ IN_TXN_ABORT(env, NULL, txn_change, 0, {
+ { int chk_r = db->change_descriptor(db, txn_change, &change_descriptor, 0); CKERR(chk_r); }
+ assert_desc_eight(db);
+ });
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ db_env_set_checkpoint_callback(checkpoint_callback_1, NULL);
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+
+ DBT orig_desc;
+ memset(&orig_desc, 0, sizeof(orig_desc));
+ orig_desc.size = sizeof(four_byte_desc);
+ orig_desc.data = &four_byte_desc;
+ // verify we can only set a descriptor with version 1
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ assert(db->descriptor == NULL);
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn_create, &orig_desc, 0); CKERR(chk_r); }
+ assert_desc_four(db);
+ });
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ { int chk_r = env->txn_checkpoint(env, 0, 0, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+
+ db = NULL;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_transactional_descriptor.cc b/storage/tokudb/PerconaFT/src/tests/test_transactional_descriptor.cc
new file mode 100644
index 00000000000..86350f95e40
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_transactional_descriptor.cc
@@ -0,0 +1,227 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+uint32_t four_byte_desc = 101;
+uint64_t eight_byte_desc = 10101;
+
+
+static void assert_desc_four (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(four_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == four_byte_desc);
+}
+static void assert_desc_eight (DB* db) {
+ assert(db->descriptor->dbt.size == sizeof(eight_byte_desc));
+ assert(*(uint32_t *)(db->descriptor->dbt.data) == eight_byte_desc);
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+ DB* db2 = NULL;
+
+ DBT orig_desc;
+ memset(&orig_desc, 0, sizeof(orig_desc));
+ orig_desc.size = sizeof(four_byte_desc);
+ orig_desc.data = &four_byte_desc;
+ // verify we can only set a descriptor with version 1
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ assert(db->descriptor == NULL);
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn_create, &orig_desc, 0); CKERR(chk_r); }
+ assert_desc_four(db);
+ });
+
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+ db2 = NULL;
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+
+ // verify that after closing and reopening db gets the same descriptor
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+
+ /********************************************************************/
+
+ // now lets test change_descriptor
+ DBT change_descriptor;
+ memset(&change_descriptor, 0, sizeof(change_descriptor));
+ change_descriptor.size = sizeof(eight_byte_desc);
+ change_descriptor.data = &eight_byte_desc;
+
+ // test that simple abort works
+ IN_TXN_ABORT(env, NULL, txn_change, 0, {
+ { int chk_r = db->change_descriptor(db, txn_change, &change_descriptor, 0); CKERR(chk_r); }
+ assert_desc_eight(db);
+ });
+ assert_desc_four(db);
+
+ // test that close/reopen gets the right descriptor
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+ db2 = NULL;
+
+ // test that simple commit works
+ IN_TXN_COMMIT(env, NULL, txn_change, 0, {
+ { int chk_r = db->change_descriptor(db, txn_change, &change_descriptor, 0); CKERR(chk_r); }
+ assert_desc_eight(db);
+ });
+ assert_desc_eight(db);
+
+ // test that close/reopen gets the right descriptor
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_eight(db);
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+ db2 = NULL;
+
+
+ change_descriptor.size = sizeof(four_byte_desc);
+ change_descriptor.data = &four_byte_desc;
+ // test that close then abort works
+ IN_TXN_ABORT(env, NULL, txn_change, 0, {
+ { int chk_r = db->change_descriptor(db, txn_change, &change_descriptor, 0); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_change, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+ });
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_eight(db);
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_eight(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+ db2 = NULL;
+
+ // test that close then commit works
+ IN_TXN_COMMIT(env, NULL, txn_change, 0, {
+ { int chk_r = db->change_descriptor(db, txn_change, &change_descriptor, 0); CKERR(chk_r); }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_change, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+ });
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db);
+ { int chk_r = db_create(&db2, env, 0); CKERR(chk_r); }
+ { int chk_r = db2->open(db2, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ assert_desc_four(db2);
+ { int chk_r = db2->close(db2, 0); CKERR(chk_r); }
+ db2 = NULL;
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+
+ IN_TXN_ABORT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ assert(db->descriptor == NULL);
+ { int chk_r = db->open(db, txn_create, "bar.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn_create, &change_descriptor, 0); CKERR(chk_r); }
+ // test some error cases
+ IN_TXN_COMMIT(env, txn_create, txn_create2, 0, {
+ { int chk_r = db->change_descriptor(db, txn_create, &change_descriptor, 0); CKERR2(chk_r, EINVAL); }
+ });
+ assert_desc_four(db);
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+ });
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ assert(db->descriptor == NULL);
+ { int chk_r = db->open(db, txn_create, "bar.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ { int chk_r = db->change_descriptor(db, txn_create, &change_descriptor, 0); CKERR(chk_r); }
+ assert_desc_four(db);
+ });
+ assert_desc_four(db);
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ db = NULL;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_abort5.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_abort5.cc
new file mode 100644
index 00000000000..81d9c951f14
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_abort5.cc
@@ -0,0 +1,112 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+static void
+test_txn_abort (int n) {
+ if (verbose) printf("test_txn_abort:%d\n", n);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+ int i;
+ for (i=0; i<n; i++) {
+ DBT key, val;
+ r = db->put(db, txn, dbt_init(&key, &i, sizeof i), dbt_init(&val, &i, sizeof i), 0);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+ }
+ r = txn->abort(txn);
+#if 0
+ assert(r == 0);
+#else
+ if (r != 0) printf("%s:%d:abort:%d\n", __FILE__, __LINE__, r);
+#endif
+ /* walk the db, should be empty */
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_FIRST);
+ assert(r == DB_NOTFOUND);
+ r = cursor->c_close(cursor); assert(r == 0);
+ r = txn->commit(txn, 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ int i;
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (strcmp(arg, "-v") == 0 || strcmp(arg, "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ }
+ for (i=1; i<100; i++)
+ test_txn_abort(i);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_abort5a.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_abort5a.cc
new file mode 100644
index 00000000000..fec454b8009
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_abort5a.cc
@@ -0,0 +1,132 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_txn_abort (int n) {
+ if (verbose>1) printf("%s %s:%d\n", __FILE__, __FUNCTION__, n);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+ int i;
+ for (i=0; i<n; i++) {
+ DBT key, val;
+ int i2=htonl(i*2);
+ if (verbose>2) printf("put %d\n", i*2);
+ r = db->put(db, txn, dbt_init(&key, &i2, sizeof i2), dbt_init(&val, &i, sizeof i), 0);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+ }
+ r = txn->commit(txn, 0);
+
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+ for (i=0; i<n; i++) {
+ DBT key;
+ int i2=htonl(i*2);
+ if (verbose>2) printf("del %d\n", i*2);
+ r = db->del(db, txn, dbt_init(&key, &i2, sizeof i2), 0);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+ }
+ r = txn->abort(txn);
+ if (r != 0) printf("%s:%d:abort:%d\n", __FILE__, __LINE__, r);
+ assert(r == 0);
+ /* walk the db, even numbers should be there */
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ for (i=0; 1; i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r!=0) break;
+ if (verbose>2) printf("%u present\n", (uint32_t)ntohl(*(int*)key.data));
+ assert(key.size==4);
+ assert(ntohl(*(int*)key.data)==(unsigned int)(2*i));
+ }
+ assert(i==n);
+ r = cursor->c_close(cursor); assert(r == 0);
+ r = txn->commit(txn, 0);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ int i;
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (strcmp(arg, "-v") == 0 || strcmp(arg, "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ }
+ if (verbose>0) printf("%s", __FILE__); if (verbose>1) printf("\n");
+ for (i=1; i<100; i++)
+ test_txn_abort(i);
+ if (verbose>1) printf("%s OK\n", __FILE__);
+ if (verbose>0) printf(" OK\n");
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_abort6.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_abort6.cc
new file mode 100644
index 00000000000..8350830bac4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_abort6.cc
@@ -0,0 +1,162 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+#define N_TXNS 4
+
+static void
+test_txn_abort (int n, int which_guys_to_abort) {
+ if (verbose>1) printf("test_txn_abort(%d,%x)\n", n, which_guys_to_abort);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+
+ DB *db;
+ {
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+ r = txn->commit(txn, 0); assert(r == 0);
+ }
+ {
+ DB_TXN *txns[N_TXNS];
+ {
+ int j;
+ for (j=0; j<N_TXNS; j++) {
+ r = env->txn_begin(env, 0, &txns[j], 0); assert(r == 0);
+ }
+ }
+
+ {
+ int i;
+ for (i=0; i<n; i++) {
+ int j;
+ for (j=N_TXNS; j>0; j--) {
+ if (i%j==0) { // This is guaranteed to be true when j==1, so someone will do it.
+ DBT key, val;
+ r = db->put(db, txns[j-1], dbt_init(&key, &i, sizeof i), dbt_init(&val, &i, sizeof i), 0);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+ goto didit;
+ }
+ }
+ toku_hard_crash_on_purpose();
+ didit: ;
+ }
+ }
+ {
+ int j;
+ for (j=0; j<N_TXNS; j++) {
+ if (which_guys_to_abort&(1<<j)) {
+ r = txns[j]->abort(txns[j]);
+ } else {
+ r = txns[j]->commit(txns[j], 0);
+ }
+ if (r != 0) printf("%s:%d:abort:%d\n", __FILE__, __LINE__, r);
+ assert(r == 0);
+ }
+ }
+ }
+ {
+ DB_TXN *txn;
+ int i;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r==0);
+ if (verbose>1) printf("Now see what's there: which_guys_to_abort=%x: ", which_guys_to_abort);
+ for (i=0; i<n; i++) {
+ DBT key,val;
+ memset(&val, 0, sizeof val);
+ r = db->get(db, txn, dbt_init(&key, &i, sizeof i), &val, 0);
+ if (r==0) { if (verbose>1) printf(" %d", i); }
+ }
+ if (verbose>1) printf("\n");
+ for (i=0; i<n; i++) {
+ DBT key,val;
+ memset(&val, 0, sizeof val);
+ r = db->get(db, txn, dbt_init(&key, &i, sizeof i), &val, 0);
+ int j;
+ for (j=N_TXNS; j>0; j--) {
+ if (i%j==0) {
+ if (which_guys_to_abort&(1<<(j-1))) assert(r==DB_NOTFOUND);
+ else assert(r==0);
+ break;
+ }
+ }
+ }
+ r = txn->commit(txn, 0); assert(r==0);
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ int i,j;
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (strcmp(arg, "-v") == 0 || strcmp(arg, "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ }
+ if (verbose>0) printf("%s:", __FILE__);
+ if (verbose==1) printf("\n");
+ for (j=0; j<(1<<N_TXNS); j++)
+ for (i=1; i<100; i*=2)
+ test_txn_abort(i, j);
+ if (verbose>0) printf("OK\n");
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_abort7.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_abort7.cc
new file mode 100644
index 00000000000..fb428f437b1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_abort7.cc
@@ -0,0 +1,119 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+static void
+test_abort_create (void) {
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stdout);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+
+ {
+ char *filename;
+ {
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, "test.db", sizeof("test.db"));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname);
+ CKERR(r);
+ CAST_FROM_VOIDP(filename, iname.data);
+ assert(filename);
+ }
+ toku_struct_stat statbuf;
+ char fullfile[TOKU_PATH_MAX+1];
+ r = toku_stat(toku_path_join(fullfile, 2, TOKU_TEST_FILENAME, filename), &statbuf);
+ assert(r==0);
+ toku_free(filename);
+ }
+
+ r = db->close(db, 0);
+ r = txn->abort(txn); assert(r == 0);
+
+ {
+ {
+ DBT dname;
+ DBT iname;
+ dbt_init(&dname, "test.db", sizeof("test.db"));
+ dbt_init(&iname, NULL, 0);
+ iname.flags |= DB_DBT_MALLOC;
+ r = env->get_iname(env, &dname, &iname);
+ CKERR2(r, DB_NOTFOUND);
+ }
+ toku_struct_stat statbuf;
+ char fullfile[TOKU_PATH_MAX+1];
+ r = toku_stat(toku_path_join(fullfile, 2, TOKU_TEST_FILENAME, "test.db"), &statbuf);
+ assert(r!=0);
+ assert(errno==ENOENT);
+ }
+
+ r = env->close(env, 0); assert(r == 0);
+
+
+
+}
+
+int
+test_main(int UU(argc), char UU(*const argv[])) {
+ test_abort_create();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_begin_commit.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_begin_commit.cc
new file mode 100644
index 00000000000..fb9cb7cd72e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_begin_commit.cc
@@ -0,0 +1,70 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <sys/stat.h>
+#include <db.h>
+
+
+int
+test_main(int UU(argc), char UU(*const argv[])) {
+ int r;
+ DB_ENV *env;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, 0, &txn, 0);
+ assert(r == 0);
+
+ r = txn->commit(txn, 0);
+ assert(r == 0);
+
+r = env->close(env, 0);
+ assert(r == 0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_close_before_commit.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_close_before_commit.cc
new file mode 100644
index 00000000000..87d76db5c3a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_close_before_commit.cc
@@ -0,0 +1,89 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+// Recreate a mysqld crash by closing and opening a db within a transaction.
+// The crash occurs when writing a dirty cachetable pair, so we insert one
+// row.
+static void
+test_txn_close_before_commit (void) {
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ int r;
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stdout);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, NULL, "test.db", 0, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+
+ DBT key, val;
+ int k = 1, v = 1;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+
+ // Close before commit
+ r = db->close(db, 0); assert(r == 0);
+
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int UU(argc), char UU(*const argv[])) {
+ test_txn_close_before_commit();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_close_before_prepare_commit.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_close_before_prepare_commit.cc
new file mode 100644
index 00000000000..80525fd3e1c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_close_before_prepare_commit.cc
@@ -0,0 +1,92 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+// Recreate a mysqld crash by closing and opening a db within a transaction.
+// The crash occurs when writing a dirty cachetable pair, so we insert one
+// row.
+static void
+test_txn_close_before_prepare_commit (void) {
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ int r;
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stdout);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_LOCK + DB_INIT_TXN + DB_PRIVATE + DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ if (r != 0) printf("%s:%d:%d:%s\n", __FILE__, __LINE__, r, db_strerror(r));
+ assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->open(db, NULL, "test.db", 0, DB_BTREE, DB_CREATE|DB_AUTO_COMMIT, S_IRWXU+S_IRWXG+S_IRWXO); assert(r == 0);
+
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); assert(r == 0);
+
+ DBT key, val;
+ int k = 1, v = 1;
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+
+ // Close before commit
+ r = db->close(db, 0); assert(r == 0);
+
+ uint8_t gid[DB_GID_SIZE];
+ memset(gid, 1, DB_GID_SIZE);
+ r = txn->prepare(txn, gid, 0); assert(r == 0);
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int UU(argc), char UU(*const argv[])) {
+ test_txn_close_before_prepare_commit();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_cursor_last.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_cursor_last.cc
new file mode 100644
index 00000000000..5571281f152
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_cursor_last.cc
@@ -0,0 +1,249 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static int
+db_put (DB *db, DB_TXN *txn, int k, int v) {
+ DBT key, val;
+ return db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), DB_NOOVERWRITE);
+}
+
+static const char *db_error(int error) {
+ static char errorbuf[32];
+ switch (error) {
+ case DB_NOTFOUND: return "DB_NOTFOUND";
+ case DB_LOCK_DEADLOCK: return "DB_LOCK_DEADLOCK";
+ case DB_LOCK_NOTGRANTED: return "DB_LOCK_NOTGRANTED";
+ case DB_KEYEXIST: return "DB_KEYEXIST";
+ default:
+ sprintf(errorbuf, "%d", error);
+ return errorbuf;
+ }
+}
+
+/* t1 t2 l1 l2 p1 p2 c1 c2 */
+static void
+test_txn_cursor_last_1 (int nrows) {
+ if (verbose) printf("test_txn_cursor_last_1:%d\n", nrows);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.txn.cursor.last.1.ft_handle";
+
+ /* create the dup database file */
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK|DB_INIT_LOG |DB_THREAD |DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,stderr); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE+DB_AUTO_COMMIT, 0666); assert(r == 0);
+ int i;
+ for (i=0; i<nrows; i++) {
+ int k = htonl(i);
+ int v = htonl(i);
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+ }
+
+ DB_TXN *t1;
+ r = env->txn_begin(env, null_txn, &t1, 0); assert(r == 0);
+ if (verbose) printf("t1:begin\n");
+
+ DBC *c1;
+ r = db->cursor(db, t1, &c1, 0); assert(r == 0);
+
+ DB_TXN *t2;
+ r = env->txn_begin(env, null_txn, &t2, 0); assert(r == 0);
+ if (verbose) printf("t2:begin\n");
+
+ DBC *c2;
+ r = db->cursor(db, t2, &c2, 0); assert(r == 0);
+
+ DBT k1; memset(&k1, 0, sizeof k1);
+ DBT v1; memset(&v1, 0, sizeof v1);
+ r = c1->c_get(c1, &k1, &v1, DB_LAST);
+ if (verbose) printf("c1:last:%s\n", db_error(r));
+
+ r = c1->c_close(c1); assert(r == 0);
+
+ DBT k2; memset(&k2, 0, sizeof k2);
+ DBT v2; memset(&v2, 0, sizeof v2);
+ r = c2->c_get(c2, &k2, &v2, DB_LAST);
+ if (verbose) printf("c2:last:%s\n", db_error(r));
+
+ r = c2->c_close(c2); assert(r == 0);
+
+ int r1 = db_put(db, t1, htonl(nrows), htonl(nrows));
+ if (verbose) printf("t1:put:%s\n", db_error(r1));
+
+ int r2 = db_put(db, t2, htonl(nrows), htonl(nrows));
+ if (verbose) printf("t2:put:%s\n", db_error(r2));
+
+ if (r1 == 0) {
+ r = t1->commit(t1, 0);
+ if (verbose) printf("t1:commit:%s\n", db_error(r));
+ } else {
+ r = t1->abort(t1);
+ if (verbose) printf("t1:abort:%s\n", db_error(r));
+ }
+
+ if (r2 == 0) {
+ r = t2->commit(t2, 0);
+ if (verbose) printf("t2:commit:%s\n", db_error(r));
+ } else {
+ r = t2->abort(t2);
+ if (verbose) printf("t2:abort:%s\n", db_error(r));
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+/* t1 t2 l1 p1 l2 c1 p2 c2 */
+static void
+test_txn_cursor_last_2 (int nrows) {
+ if (verbose) printf("test_txn_cursor_last_2:%d\n", nrows);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.txn.cursor.last.1.ft_handle";
+
+ /* create the dup database file */
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK|DB_INIT_LOG|DB_THREAD|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,stderr); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE+DB_AUTO_COMMIT, 0666); assert(r == 0);
+ int i;
+ for (i=0; i<nrows; i++) {
+ int k = htonl(i);
+ int v = htonl(i);
+ DBT key, val;
+ r = db->put(db, null_txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+ }
+
+ DB_TXN *t1;
+ r = env->txn_begin(env, null_txn, &t1, 0); assert(r == 0);
+ if (verbose) printf("t1:begin\n");
+
+ DBC *c1;
+ r = db->cursor(db, t1, &c1, 0); assert(r == 0);
+
+ DB_TXN *t2;
+ r = env->txn_begin(env, null_txn, &t2, 0); assert(r == 0);
+ if (verbose) printf("t2:begin\n");
+
+ DBC *c2;
+ r = db->cursor(db, t2, &c2, 0); assert(r == 0);
+
+ DBT k1; memset(&k1, 0, sizeof k1);
+ DBT v1; memset(&v1, 0, sizeof v1);
+ r = c1->c_get(c1, &k1, &v1, DB_LAST);
+ if (verbose) printf("c1:last:%s\n", db_error(r));
+
+ r = c1->c_close(c1); assert(r == 0);
+
+ int r1 = db_put(db, t1, htonl(nrows), htonl(nrows));
+ if (verbose) printf("t1:put:%s\n", db_error(r1));
+
+ DBT k2; memset(&k2, 0, sizeof k2);
+ DBT v2; memset(&v2, 0, sizeof v2);
+ r = c2->c_get(c2, &k2, &v2, DB_LAST);
+ if (verbose) printf("c2:last:%s\n", db_error(r));
+
+ r = c2->c_close(c2); assert(r == 0);
+
+ if (r1 == 0) {
+ r = t1->commit(t1, 0);
+ if (verbose) printf("t1:commit:%s\n", db_error(r));
+ } else {
+ r = t1->abort(t1);
+ if (verbose) printf("t1:abort:%s\n", db_error(r));
+ }
+
+ int r2 = db_put(db, t2, htonl(nrows), htonl(nrows));
+ if (verbose) printf("t2:put:%s\n", db_error(r2));
+
+ if (r2 == 0) {
+ r = t2->commit(t2, 0);
+ if (verbose) printf("t2:commit:%s\n", db_error(r));
+ } else {
+ r = t2->abort(t2);
+ if (verbose) printf("t2:abort:%s\n", db_error(r));
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ test_txn_cursor_last_1(0);
+ test_txn_cursor_last_1(1);
+ test_txn_cursor_last_2(0);
+ test_txn_cursor_last_2(1);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested1.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested1.cc
new file mode 100644
index 00000000000..d83cd15d37d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested1.cc
@@ -0,0 +1,173 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <ft/txn/xids.h>
+#define MAX_NEST MAX_NESTED_TRANSACTIONS
+
+
+/*********************
+ *
+ * Purpose of this test is to exercise nested transactions in a basic way:
+ * Create MAX nested transactions, inserting a value at each level, verify:
+ *
+ * for i = 1 to MAX
+ * - txnid = begin()
+ * - txns[i] = txnid
+ * - insert, query
+ *
+ * for i = 1 to MAX
+ * - txnid = txns[MAX - i - 1]
+ * - commit or abort(txnid), query
+ *
+ */
+
+static DB *db;
+static DB_ENV *env;
+
+static void
+setup_db (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, int_dbt_cmp); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+}
+
+
+static void
+close_db (void) {
+ int r;
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+static void
+test_txn_nesting (int depth) {
+ int r;
+ if (verbose) { fprintf(stderr, "%s (%s):%d [depth = %d]\n", __FILE__, __FUNCTION__, __LINE__, depth); fflush(stderr); }
+
+ DBT key, val, observed_val;
+ dbt_init(&observed_val, NULL, 0);
+ int i;
+
+ DB_TXN * txns[depth];
+ DB_TXN * parent = NULL;
+
+ int vals[depth];
+
+ int mykey = 42;
+ dbt_init(&key, &mykey, sizeof mykey);
+
+
+ for (i = 0; i < depth; i++){
+ DB_TXN * this_txn;
+
+ if (verbose)
+ printf("Begin txn at level %d\n", i);
+ vals[i] = i;
+ dbt_init(&val, &vals[i], sizeof i);
+ r = env->txn_begin(env, parent, &this_txn, 0); CKERR(r);
+ txns[i] = this_txn;
+ parent = this_txn; // will be parent in next iteration
+ r = db->put(db, this_txn, &key, &val, 0); CKERR(r);
+
+ r = db->get(db, this_txn, &key, &observed_val, 0); CKERR(r);
+ assert(int_dbt_cmp(db, &val, &observed_val) == 0);
+ }
+
+ int which_val = depth-1;
+ for (i = depth-1; i >= 0; i--) {
+ //Query, verify the correct value is stored.
+ //Close (abort/commit) innermost transaction
+
+ if (verbose)
+ printf("Commit txn at level %d\n", i);
+
+ dbt_init(&observed_val, NULL, 0);
+ r = db->get(db, txns[i], &key, &observed_val, 0); CKERR(r);
+ dbt_init(&val, &vals[which_val], sizeof i);
+ assert(int_dbt_cmp(db, &val, &observed_val) == 0);
+
+ if (i % 2) {
+ r = txns[i]->commit(txns[i], DB_TXN_NOSYNC); CKERR(r);
+ //which_val does not change (it gets promoted)
+ }
+ else {
+ r = txns[i]->abort(txns[i]); CKERR(r);
+ which_val = i - 1;
+ }
+ txns[i] = NULL;
+ }
+ //Query, verify the correct value is stored.
+ r = db->get(db, NULL, &key, &observed_val, 0);
+ if (which_val == -1) CKERR2(r, DB_NOTFOUND);
+ else {
+ CKERR(r);
+ dbt_init(&val, &vals[which_val], sizeof i);
+ assert(int_dbt_cmp(db, &val, &observed_val) == 0);
+ }
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ setup_db();
+ test_txn_nesting(MAX_NEST);
+ close_db();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested2.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested2.cc
new file mode 100644
index 00000000000..ffb9cb67aa0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested2.cc
@@ -0,0 +1,245 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <db.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+
+#include "src/tests/test.h"
+
+#include <ft/txn/xids.h>
+
+#define MAX_NEST MAX_TRANSACTION_RECORDS
+#define MAX_SIZE MAX_TRANSACTION_RECORDS
+
+uint8_t valbufs[MAX_NEST][MAX_SIZE];
+DBT vals [MAX_NEST];
+uint8_t keybuf [MAX_SIZE];
+DBT key;
+DB_TXN *txns [MAX_NEST];
+DB_TXN *txn_query;
+int which_expected;
+
+static void
+fillrandom(uint8_t buf[MAX_SIZE], uint32_t length) {
+ assert(length < MAX_SIZE);
+ uint32_t i;
+ for (i = 0; i < length; i++) {
+ buf[i] = random() & 0xFF;
+ }
+}
+
+static void
+initialize_values (void) {
+ int nest_level;
+ for (nest_level = 0; nest_level < MAX_NEST; nest_level++) {
+ fillrandom(valbufs[nest_level], nest_level);
+ dbt_init(&vals[nest_level], &valbufs[nest_level][0], nest_level);
+ }
+ uint32_t len = random() % MAX_SIZE;
+ fillrandom(keybuf, len);
+ dbt_init(&key, &keybuf[0], len);
+}
+
+
+/*********************
+ *
+ * Purpose of this test is to verify nested transactions (support right number of possible values)
+for test = 1 to MAX
+ create empty db
+ for nesting_level = 1 to MAX
+ - begin txn
+ - insert a value/len unique to this txn
+ - query
+ abort txn (MAX-test) (test-th innermost) // for test=1 don't abort anything
+ commit txn 1 (outermost) // for test = MAX don't commit anything
+ query // only query that really matters
+ */
+
+static DB *db;
+static DB_ENV *env;
+
+static void
+setup_db (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+}
+
+
+static void
+close_db (void) {
+ int r;
+ r = txn_query->commit(txn_query, 0);
+ CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+static void
+verify_val(void) {
+ int r;
+ DBT observed_val;
+ dbt_init(&observed_val, NULL, 0);
+ r = db->get(db, txn_query, &key, &observed_val, 0);
+ if (which_expected==-1)
+ CKERR2(r, DB_NOTFOUND);
+ else {
+ CKERR(r);
+ assert(observed_val.size == vals[which_expected].size);
+ assert(memcmp(observed_val.data, vals[which_expected].data, vals[which_expected].size) == 0);
+ }
+}
+
+static void
+initialize_db(void) {
+ int r;
+ r = env->txn_begin(env, NULL, &txn_query, DB_READ_UNCOMMITTED);
+ CKERR(r);
+ which_expected = -1;
+ verify_val();
+ //Put in a 'committed value'
+ r = db->put(db, NULL, &key, &vals[0], 0);
+ CKERR(r);
+ txns[0] = NULL;
+
+ int i;
+ which_expected = 0;
+ for (i = 1; i < MAX_NEST; i++) {
+ r = env->txn_begin(env, txns[i-1], &txns[i], 0);
+ CKERR(r);
+ verify_val();
+ r = db->put(db, txns[i], &key, &vals[i], 0);
+ CKERR(r);
+ which_expected = i;
+ verify_val();
+ }
+}
+
+static void
+test_txn_nested_shortcut (int abort_at_depth) {
+ int r;
+ if (verbose) { fprintf(stderr, "%s (%s):%d [abortdepth = %d]\n", __FILE__, __FUNCTION__, __LINE__, abort_at_depth); fflush(stderr); }
+
+ setup_db();
+ initialize_db();
+
+ which_expected = MAX_NEST-1;
+ verify_val();
+
+ assert(abort_at_depth > 0); //Cannot abort 'committed' txn.
+ assert(abort_at_depth <= MAX_NEST); //must be in range
+ if (abort_at_depth < MAX_NEST) {
+ //MAX_NEST means no abort
+ DB_TXN *abort_txn = txns[abort_at_depth];
+ r = abort_txn->abort(abort_txn);
+ CKERR(r);
+ which_expected = abort_at_depth - 1;
+ verify_val();
+ }
+ if (abort_at_depth > 1) {
+ //abort_at_depth 1 means abort the whole thing (nothing left to commit)
+ DB_TXN *commit_txn = txns[1];
+ r = commit_txn->commit(commit_txn, DB_TXN_NOSYNC);
+ CKERR(r);
+ verify_val();
+ }
+ close_db();
+}
+
+static void
+test_txn_nested_slow (int abort_at_depth) {
+ int r;
+ if (verbose) { fprintf(stderr, "%s (%s):%d [abortdepth = %d]\n", __FILE__, __FUNCTION__, __LINE__, abort_at_depth); fflush(stderr); }
+
+ setup_db();
+ initialize_db();
+
+ which_expected = MAX_NEST-1;
+ verify_val();
+
+ assert(abort_at_depth > 0); //Cannot abort 'committed' txn.
+ assert(abort_at_depth <= MAX_NEST); //must be in range
+ //MAX_NEST means no abort
+ int nest;
+ for (nest = MAX_NEST - 1; nest >= abort_at_depth; nest--) {
+ DB_TXN *abort_txn = txns[nest];
+ r = abort_txn->abort(abort_txn);
+ CKERR(r);
+ which_expected = nest - 1;
+ verify_val();
+ }
+ //which_expected does not change anymore
+ for (nest = abort_at_depth-1; nest > 0; nest--) {
+ DB_TXN *commit_txn = txns[nest];
+ r = commit_txn->commit(commit_txn, DB_TXN_NOSYNC);
+ CKERR(r);
+ verify_val();
+ }
+ close_db();
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ initialize_values();
+ int i;
+ for (i = 1; i <= MAX_NEST; i++) {
+ test_txn_nested_shortcut(i);
+ test_txn_nested_slow(i);
+ }
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested3.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested3.cc
new file mode 100644
index 00000000000..8794c05ce7d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested3.cc
@@ -0,0 +1,281 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <ft/txn/xids.h>
+#define MAX_NEST MAX_TRANSACTION_RECORDS
+#define MAX_SIZE MAX_TRANSACTION_RECORDS
+
+/*********************
+ *
+ * Purpose of this test is to verify nested transactions (support right number of possible values)
+create empty db
+for test = 1 to MAX
+ for nesting level 0
+ - randomly insert or not
+ for nesting_level = 1 to MAX
+ - begin txn
+ - randomly one of (insert, delete, do nothing)
+ - if insert, use a value/len unique to this txn
+ - query to verify
+ for nesting level = MAX to 1
+ - randomly abort or commit each transaction
+ - query to verify
+delete db
+ */
+
+
+enum { TYPE_DELETE = 1, TYPE_INSERT, TYPE_PLACEHOLDER };
+
+uint8_t valbufs[MAX_NEST][MAX_SIZE];
+DBT vals [MAX_NEST];
+uint8_t keybuf [MAX_SIZE];
+DBT key;
+uint8_t types [MAX_NEST];
+DB_TXN *txns [MAX_NEST];
+DB_TXN *txn_query;
+int which_expected;
+
+static void
+fillrandom(uint8_t buf[MAX_SIZE], uint32_t length) {
+ assert(length < MAX_SIZE);
+ uint32_t i;
+ for (i = 0; i < length; i++) {
+ buf[i] = random() & 0xFF;
+ }
+}
+
+static void
+initialize_values (void) {
+ int nest_level;
+ for (nest_level = 0; nest_level < MAX_NEST; nest_level++) {
+ fillrandom(valbufs[nest_level], nest_level);
+ dbt_init(&vals[nest_level], &valbufs[nest_level][0], nest_level);
+ }
+ uint32_t len = random() % MAX_SIZE;
+ fillrandom(keybuf, len);
+ dbt_init(&key, &keybuf[0], len);
+}
+
+
+static DB *db;
+static DB_ENV *env;
+
+static void
+setup_db (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ r = env->txn_begin(env, NULL, &txn_query, DB_READ_UNCOMMITTED);
+ CKERR(r);
+}
+
+
+static void
+close_db (void) {
+ int r;
+ r = txn_query->commit(txn_query, 0);
+ CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+static void
+verify_val(uint8_t nest_level) {
+ assert(nest_level < MAX_NEST);
+ if (types[nest_level] == TYPE_PLACEHOLDER) {
+ assert(nest_level > 0);
+ return verify_val(nest_level - 1);
+ }
+ int r;
+ DBT observed_val;
+ dbt_init(&observed_val, NULL, 0);
+ r = db->get(db, txn_query, &key, &observed_val, 0);
+ if (types[nest_level] == TYPE_INSERT) {
+ CKERR(r);
+ assert(observed_val.size == vals[nest_level].size);
+ assert(memcmp(observed_val.data, vals[nest_level].data, vals[nest_level].size) == 0);
+ }
+ else {
+ assert(types[nest_level] == TYPE_DELETE);
+ CKERR2(r, DB_NOTFOUND);
+ }
+}
+
+static uint8_t
+randomize_no_placeholder_type(void) {
+ int r;
+ r = random() % 2;
+ switch (r) {
+ case 0:
+ return TYPE_INSERT;
+ case 1:
+ return TYPE_DELETE;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+static uint8_t
+randomize_type(void) {
+ int r;
+ do {
+ r = random() % 4;
+ } while (r >= 3); //Generate uniformly random 0-2
+ switch (r) {
+ case 0:
+ return TYPE_INSERT;
+ case 1:
+ return TYPE_DELETE;
+ case 2:
+ return TYPE_PLACEHOLDER;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+static void
+start_txn_and_maybe_insert_or_delete(uint8_t nest) {
+ int r;
+ if (nest == 0) {
+ types[nest] = randomize_no_placeholder_type();
+ assert(types[nest] != TYPE_PLACEHOLDER);
+ //Committed entry is autocommitted by not providing the txn
+ txns[nest] = NULL;
+ }
+ else {
+ types[nest] = randomize_type();
+ r = env->txn_begin(env, txns[nest-1], &txns[nest], 0);
+ CKERR(r);
+ }
+ switch (types[nest]) {
+ case TYPE_INSERT:
+ r = db->put(db, txns[nest], &key, &vals[nest], 0);
+ CKERR(r);
+ break;
+ case TYPE_DELETE:
+ r = db->del(db, txns[nest], &key, DB_DELETE_ANY);
+ CKERR(r);
+ break;
+ case TYPE_PLACEHOLDER:
+ //Do Nothing.
+ break;
+ default:
+ assert(false);
+ }
+ verify_val(nest);
+}
+
+static void
+initialize_db(void) {
+ types[0] = TYPE_DELETE; //Not yet inserted
+ verify_val(0);
+ int i;
+ for (i = 0; i < MAX_NEST; i++) {
+ start_txn_and_maybe_insert_or_delete(i);
+ }
+}
+
+static void
+test_txn_nested_jumble (int iteration) {
+ int r;
+ if (verbose) { fprintf(stderr, "%s (%s):%d [iteration # %d]\n", __FILE__, __FUNCTION__, __LINE__, iteration); fflush(stderr); }
+
+ initialize_db();
+
+ //BELOW IS OLD CODE
+ int index_of_expected_value = MAX_NEST - 1;
+ int nest_level;
+ for (nest_level = MAX_NEST - 1; nest_level > 0; nest_level--) {
+ int do_abort = random() & 0x1;
+ if (do_abort) {
+ r = txns[nest_level]->abort(txns[nest_level]);
+ CKERR(r);
+ index_of_expected_value = nest_level - 1;
+ }
+ else {
+ r = txns[nest_level]->commit(txns[nest_level], DB_TXN_NOSYNC);
+ CKERR(r);
+ //index of expected value unchanged
+ }
+ txns[nest_level] = NULL;
+ verify_val(index_of_expected_value);
+ }
+ //Clean out dictionary
+
+ types[0] = TYPE_DELETE;
+ r = db->del(db, NULL, &key, DB_DELETE_ANY);
+ CKERR(r);
+ verify_val(0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ initialize_values();
+ int i;
+ setup_db();
+ for (i = 0; i < 64; i++) {
+ test_txn_nested_jumble(i);
+ }
+ close_db();
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested4.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested4.cc
new file mode 100644
index 00000000000..fa4488ab10a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested4.cc
@@ -0,0 +1,367 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <ft/txn/xids.h>
+#define MAX_NEST MAX_TRANSACTION_RECORDS
+#define MAX_SIZE MAX_TRANSACTION_RECORDS
+
+/*********************
+ *
+ * Purpose of this test is to verify nested transactions, including support for implicit promotion
+ * in the presence of placeholders and branched trees of transactions.
+ *
+create empty db
+for test = 1 to MAX
+ for nesting level 0
+ - randomly insert or not
+ for nesting_level = 1 to MAX
+ - begin txn
+ - randomly perform four operations, each of which is one of (insert, delete, do nothing)
+ - if insert, use a value/len unique to this txn
+ - query to verify
+ for nesting level = MAX to 1
+ - randomly abort or commit each transaction or
+ - insert or delete at same level (followed by either abort/commit)
+ - branch (add more child txns similar to above)
+ - query to verify
+delete db
+ *
+ */
+
+
+enum { TYPE_DELETE = 1, TYPE_INSERT, TYPE_PLACEHOLDER };
+
+uint8_t valbufs[MAX_NEST][MAX_SIZE];
+DBT vals [MAX_NEST];
+uint8_t keybuf [MAX_SIZE];
+DBT key;
+uint8_t types [MAX_NEST];
+uint8_t currval[MAX_NEST];
+DB_TXN *txns [MAX_NEST];
+DB_TXN *txn_query;
+DB_TXN *patient_txn;
+int which_expected;
+
+static void
+fillrandom(uint8_t buf[MAX_SIZE], uint32_t length) {
+ assert(length < MAX_SIZE);
+ uint32_t i;
+ for (i = 0; i < length; i++) {
+ buf[i] = random() & 0xFF;
+ }
+}
+
+static void
+initialize_values (void) {
+ int nest_level;
+ for (nest_level = 0; nest_level < MAX_NEST; nest_level++) {
+ fillrandom(valbufs[nest_level], nest_level);
+ dbt_init(&vals[nest_level], &valbufs[nest_level][0], nest_level);
+ }
+ uint32_t len = random() % MAX_SIZE;
+ fillrandom(keybuf, len);
+ dbt_init(&key, &keybuf[0], len);
+}
+
+
+static DB *db;
+static DB_ENV *env;
+
+static void
+setup_db (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ r = env->txn_begin(env, NULL, &txn_query, DB_READ_UNCOMMITTED);
+ CKERR(r);
+}
+
+
+static void
+close_db (void) {
+ int r;
+ r = txn_query->commit(txn_query, 0);
+ CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+static void
+verify_val(uint8_t nest_level) {
+ assert(nest_level < MAX_NEST);
+ if (nest_level>0) assert(txns[nest_level]);
+ assert(types[nest_level] != TYPE_PLACEHOLDER);
+ int r;
+ DBT observed_val;
+ dbt_init(&observed_val, NULL, 0);
+ r = db->get(db, txn_query, &key, &observed_val, 0);
+ if (types[nest_level] == TYPE_INSERT) {
+ CKERR(r);
+ int idx = currval[nest_level];
+ assert(observed_val.size == vals[idx].size);
+ assert(memcmp(observed_val.data, vals[idx].data, vals[idx].size) == 0);
+ }
+ else {
+ assert(types[nest_level] == TYPE_DELETE);
+ CKERR2(r, DB_NOTFOUND);
+ }
+}
+
+static uint8_t
+randomize_no_placeholder_type(void) {
+ int r;
+ r = random() % 2;
+ switch (r) {
+ case 0:
+ return TYPE_INSERT;
+ case 1:
+ return TYPE_DELETE;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+static uint8_t
+randomize_type(void) {
+ int r;
+ r = random() % 4;
+ switch (r) {
+ case 0:
+ return TYPE_INSERT;
+ case 1:
+ return TYPE_DELETE;
+ case 2:
+ case 3:
+ return TYPE_PLACEHOLDER;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+static void
+maybe_insert_or_delete(uint8_t nest, int type) {
+ int r;
+ if (nest>0) assert(txns[nest]);
+ types[nest] = type;
+ currval[nest] = nest;
+ switch (types[nest]) {
+ case TYPE_INSERT:
+ r = db->put(db, txns[nest], &key, &vals[nest], 0);
+ CKERR(r);
+ break;
+ case TYPE_DELETE:
+ r = db->del(db, txns[nest], &key, DB_DELETE_ANY);
+ CKERR(r);
+ break;
+ case TYPE_PLACEHOLDER:
+ types[nest] = types[nest - 1];
+ currval[nest] = currval[nest-1];
+ break;
+ default:
+ assert(false);
+ }
+ verify_val(nest);
+}
+
+static void
+start_txn_and_maybe_insert_or_delete(uint8_t nest) {
+ int iteration;
+ int r;
+ for (iteration = 0; iteration < 4; iteration++) {
+ bool skip = false;
+ if (nest == 0) {
+ types[nest] = randomize_no_placeholder_type();
+ assert(types[nest] != TYPE_PLACEHOLDER);
+ //Committed entry is autocommitted by not providing the txn
+ txns[nest] = NULL;
+ }
+ else {
+ if (iteration == 0) {
+ types[nest] = randomize_type();
+ r = env->txn_begin(env, txns[nest-1], &txns[nest], 0);
+ CKERR(r);
+ if (types[nest] == TYPE_PLACEHOLDER) skip = true;
+ }
+ else {
+ types[nest] = randomize_no_placeholder_type();
+ assert(types[nest] != TYPE_PLACEHOLDER);
+ }
+ }
+ maybe_insert_or_delete(nest, types[nest]);
+ assert(types[nest] != TYPE_PLACEHOLDER);
+ if (skip) break;
+ }
+}
+
+static void
+initialize_db(void) {
+ types[0] = TYPE_DELETE; //Not yet inserted
+ verify_val(0);
+ int i;
+ for (i = 0; i < MAX_NEST; i++) {
+ start_txn_and_maybe_insert_or_delete(i);
+ }
+}
+
+static void
+test_txn_nested_jumble (int iteration) {
+ int r;
+ if (verbose) { fprintf(stderr, "%s (%s):%d [iteration # %d]\n", __FILE__, __FUNCTION__, __LINE__, iteration); fflush(stderr); }
+
+ initialize_db();
+ r = env->txn_begin(env, NULL, &patient_txn, 0);
+ CKERR(r);
+
+ int index_of_expected_value = MAX_NEST - 1;
+ int nest_level = MAX_NEST - 1;
+ int min_allowed_branch_level = MAX_NEST - 2;
+futz_with_stack:
+ while (nest_level > 0) {
+ int operation = random() % 4;
+ switch (operation) {
+ case 0:
+ //abort
+ r = txns[nest_level]->abort(txns[nest_level]);
+ CKERR(r);
+ index_of_expected_value = nest_level - 1;
+ txns[nest_level] = NULL;
+ nest_level--;
+ verify_val(index_of_expected_value);
+ break;
+ case 1:
+ //commit
+ r = txns[nest_level]->commit(txns[nest_level], DB_TXN_NOSYNC);
+ CKERR(r);
+ currval[nest_level-1] = currval[index_of_expected_value];
+ types[nest_level-1] = types[index_of_expected_value];
+ index_of_expected_value = nest_level - 1;
+ txns[nest_level] = NULL;
+ nest_level--;
+ verify_val(index_of_expected_value);
+ break;
+ case 2:;
+ //do more work with this guy
+ int type;
+ type = randomize_no_placeholder_type();
+ maybe_insert_or_delete(nest_level, type);
+ index_of_expected_value = nest_level;
+ continue; //transaction is still alive
+ case 3:
+ if (min_allowed_branch_level >= nest_level) {
+ //start new subtree
+ int max = nest_level + 4;
+ if (MAX_NEST - 1 < max) {
+ max = MAX_NEST - 1;
+ assert(max > nest_level);
+ }
+ int branch_level;
+ for (branch_level = nest_level + 1; branch_level <= max; branch_level++) {
+ start_txn_and_maybe_insert_or_delete(branch_level);
+ }
+ nest_level = max;
+ min_allowed_branch_level--;
+ index_of_expected_value = nest_level;
+ }
+ continue; //transaction is still alive
+ default:
+ assert(false);
+ }
+ }
+ //All transactions that have touched this key are finished.
+ assert(nest_level == 0);
+ if (min_allowed_branch_level >= 0) {
+ //start new subtree
+ int max = 4;
+ assert(patient_txn);
+ txns[1] = patient_txn;
+ patient_txn = NULL;
+ maybe_insert_or_delete(1, randomize_no_placeholder_type());
+ int branch_level;
+ for (branch_level = 2; branch_level <= max; branch_level++) {
+ start_txn_and_maybe_insert_or_delete(branch_level);
+ }
+ nest_level = max;
+ min_allowed_branch_level = -1;
+ index_of_expected_value = nest_level;
+ goto futz_with_stack;
+ }
+
+ //Clean out dictionary
+
+ types[0] = TYPE_DELETE;
+ r = db->del(db, NULL, &key, DB_DELETE_ANY);
+ CKERR(r);
+ verify_val(0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ initialize_values();
+ int i;
+ setup_db();
+ for (i = 0; i < 64; i++) {
+ test_txn_nested_jumble(i);
+ }
+ close_db();
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested5.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested5.cc
new file mode 100644
index 00000000000..0374482e982
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested5.cc
@@ -0,0 +1,386 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+#include <ft/txn/xids.h>
+#define MAX_NEST MAX_TRANSACTION_RECORDS
+#define MAX_SIZE (MAX_TRANSACTION_RECORDS + 1)
+
+/*********************
+ *
+ * Purpose of this test is to verify insert ignore (DB_NOOVERWRITE_NO_ERROR) with nested transactions,
+ * including support for implicit promotion
+ * in the presence of placeholders and branched trees of transactions.
+ *
+create empty db
+for test = 1 to MAX
+ for nesting level 0
+ - randomly insert or not
+ for nesting_level = 1 to MAX
+ - begin txn
+ - randomly perform four operations, each of which is one of (insert, delete, do nothing)
+ - if insert, use a value/len unique to this txn
+ - query to verify
+ for nesting level = MAX to 1
+ - randomly abort or commit each transaction or
+ - insert or delete at same level (followed by either abort/commit)
+ - branch (add more child txns similar to above)
+ - query to verify
+delete db
+ *
+ */
+
+
+enum { TYPE_DELETE = 1, TYPE_INSERT, TYPE_PLACEHOLDER };
+
+bool top_is_delete;
+uint8_t junkvalbuf[MAX_SIZE];
+DBT junkval;
+uint8_t valbufs[MAX_NEST][MAX_SIZE];
+DBT vals [MAX_NEST];
+uint8_t keybuf [MAX_SIZE];
+DBT key;
+uint8_t types [MAX_NEST];
+uint8_t currval[MAX_NEST];
+DB_TXN *txns [MAX_NEST];
+DB_TXN *txn_query;
+DB_TXN *patient_txn;
+int which_expected;
+
+static void
+fillrandom(uint8_t buf[MAX_SIZE], uint32_t length) {
+ assert(length < MAX_SIZE);
+ uint32_t i;
+ for (i = 0; i < length; i++) {
+ buf[i] = random() & 0xFF;
+ }
+}
+
+static void
+initialize_values (void) {
+ int nest_level;
+ for (nest_level = 0; nest_level < MAX_NEST; nest_level++) {
+ fillrandom(valbufs[nest_level], nest_level);
+ dbt_init(&vals[nest_level], &valbufs[nest_level][0], nest_level);
+ }
+ uint32_t len = random() % MAX_SIZE;
+ fillrandom(keybuf, len);
+ dbt_init(&key, &keybuf[0], len);
+
+ fillrandom(junkvalbuf, MAX_SIZE-1);
+ dbt_init(&junkval, &junkvalbuf[0], MAX_SIZE-1);
+}
+
+
+static DB *db;
+static DB_ENV *env;
+
+static void
+setup_db (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+ r = env->txn_begin(env, NULL, &txn_query, DB_READ_UNCOMMITTED);
+ CKERR(r);
+}
+
+
+static void
+close_db (void) {
+ int r;
+ r = txn_query->commit(txn_query, 0);
+ CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+}
+
+static void
+verify_val(uint8_t nest_level) {
+ assert(nest_level < MAX_NEST);
+ if (nest_level>0) assert(txns[nest_level]);
+ assert(types[nest_level] != TYPE_PLACEHOLDER);
+ int r;
+ DBT observed_val;
+ dbt_init(&observed_val, NULL, 0);
+ r = db->get(db, txn_query, &key, &observed_val, 0);
+ if (types[nest_level] == TYPE_INSERT) {
+ CKERR(r);
+ int idx = currval[nest_level];
+ assert(observed_val.size == vals[idx].size);
+ assert(memcmp(observed_val.data, vals[idx].data, vals[idx].size) == 0);
+ top_is_delete = false;
+ }
+ else {
+ assert(types[nest_level] == TYPE_DELETE);
+ CKERR2(r, DB_NOTFOUND);
+ top_is_delete = true;
+ }
+}
+
+static uint8_t
+randomize_no_placeholder_type(void) {
+ int r;
+ r = random() % 2;
+ switch (r) {
+ case 0:
+ return TYPE_INSERT;
+ case 1:
+ return TYPE_DELETE;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+static uint8_t
+randomize_type(void) {
+ int r;
+ r = random() % 4;
+ switch (r) {
+ case 0:
+ return TYPE_INSERT;
+ case 1:
+ return TYPE_DELETE;
+ case 2:
+ case 3:
+ return TYPE_PLACEHOLDER;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+static void
+maybe_insert_or_delete(uint8_t nest, int type) {
+ int r;
+ if (nest>0) assert(txns[nest]);
+ types[nest] = type;
+ currval[nest] = nest;
+ switch (types[nest]) {
+ case TYPE_INSERT:
+ if (top_is_delete) {
+ r = db->put(db, txns[nest], &key, &vals[nest], DB_NOOVERWRITE_NO_ERROR);
+ CKERR(r);
+ }
+ else {
+ r = db->put(db, txns[nest], &key, &vals[nest], 0);
+ CKERR(r);
+ }
+ r = db->put(db, txns[nest], &key, &junkval, DB_NOOVERWRITE_NO_ERROR);
+ CKERR(r);
+ top_is_delete = false;
+ break;
+ case TYPE_DELETE:
+ r = db->del(db, txns[nest], &key, DB_DELETE_ANY);
+ CKERR(r);
+ top_is_delete = true;
+ break;
+ case TYPE_PLACEHOLDER:
+ types[nest] = types[nest - 1];
+ currval[nest] = currval[nest-1];
+ break;
+ default:
+ assert(false);
+ }
+ verify_val(nest);
+}
+
+static void
+start_txn_and_maybe_insert_or_delete(uint8_t nest) {
+ int iteration;
+ int r;
+ for (iteration = 0; iteration < 4; iteration++) {
+ bool skip = false;
+ if (nest == 0) {
+ types[nest] = randomize_no_placeholder_type();
+ assert(types[nest] != TYPE_PLACEHOLDER);
+ //Committed entry is autocommitted by not providing the txn
+ txns[nest] = NULL;
+ }
+ else {
+ if (iteration == 0) {
+ types[nest] = randomize_type();
+ r = env->txn_begin(env, txns[nest-1], &txns[nest], 0);
+ CKERR(r);
+ if (types[nest] == TYPE_PLACEHOLDER) skip = true;
+ }
+ else {
+ types[nest] = randomize_no_placeholder_type();
+ assert(types[nest] != TYPE_PLACEHOLDER);
+ }
+ }
+ maybe_insert_or_delete(nest, types[nest]);
+ assert(types[nest] != TYPE_PLACEHOLDER);
+ if (skip) break;
+ }
+}
+
+static void
+initialize_db(void) {
+ types[0] = TYPE_DELETE; //Not yet inserted
+ verify_val(0);
+ int i;
+ for (i = 0; i < MAX_NEST; i++) {
+ start_txn_and_maybe_insert_or_delete(i);
+ }
+}
+
+static void
+test_txn_nested_jumble (int iteration) {
+ int r;
+ if (verbose) { fprintf(stderr, "%s (%s):%d [iteration # %d]\n", __FILE__, __FUNCTION__, __LINE__, iteration); fflush(stderr); }
+
+ initialize_db();
+ r = env->txn_begin(env, NULL, &patient_txn, 0);
+ CKERR(r);
+
+ int index_of_expected_value = MAX_NEST - 1;
+ int nest_level = MAX_NEST - 1;
+ int min_allowed_branch_level = MAX_NEST - 2;
+futz_with_stack:
+ while (nest_level > 0) {
+ int operation = random() % 4;
+ switch (operation) {
+ case 0:
+ //abort
+ r = txns[nest_level]->abort(txns[nest_level]);
+ CKERR(r);
+ index_of_expected_value = nest_level - 1;
+ txns[nest_level] = NULL;
+ nest_level--;
+ verify_val(index_of_expected_value);
+ break;
+ case 1:
+ //commit
+ r = txns[nest_level]->commit(txns[nest_level], DB_TXN_NOSYNC);
+ CKERR(r);
+ currval[nest_level-1] = currval[index_of_expected_value];
+ types[nest_level-1] = types[index_of_expected_value];
+ index_of_expected_value = nest_level - 1;
+ txns[nest_level] = NULL;
+ nest_level--;
+ verify_val(index_of_expected_value);
+ break;
+ case 2:;
+ //do more work with this guy
+ int type;
+ type = randomize_no_placeholder_type();
+ maybe_insert_or_delete(nest_level, type);
+ index_of_expected_value = nest_level;
+ continue; //transaction is still alive
+ case 3:
+ if (min_allowed_branch_level >= nest_level) {
+ //start new subtree
+ int max = nest_level + 4;
+ if (MAX_NEST - 1 < max) {
+ max = MAX_NEST - 1;
+ assert(max > nest_level);
+ }
+ int branch_level;
+ for (branch_level = nest_level + 1; branch_level <= max; branch_level++) {
+ start_txn_and_maybe_insert_or_delete(branch_level);
+ }
+ nest_level = max;
+ min_allowed_branch_level--;
+ index_of_expected_value = nest_level;
+ }
+ continue; //transaction is still alive
+ default:
+ assert(false);
+ }
+ }
+ //All transactions that have touched this key are finished.
+ assert(nest_level == 0);
+ if (min_allowed_branch_level >= 0) {
+ //start new subtree
+ int max = 4;
+ assert(patient_txn);
+ txns[1] = patient_txn;
+ patient_txn = NULL;
+ maybe_insert_or_delete(1, randomize_no_placeholder_type());
+ int branch_level;
+ for (branch_level = 2; branch_level <= max; branch_level++) {
+ start_txn_and_maybe_insert_or_delete(branch_level);
+ }
+ nest_level = max;
+ min_allowed_branch_level = -1;
+ index_of_expected_value = nest_level;
+ goto futz_with_stack;
+ }
+
+ //Clean out dictionary
+
+ types[0] = TYPE_DELETE;
+ r = db->del(db, NULL, &key, DB_DELETE_ANY);
+ CKERR(r);
+ verify_val(0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ initialize_values();
+ int i;
+ setup_db();
+ for (i = 0; i < 64; i++) {
+ test_txn_nested_jumble(i);
+ }
+ close_db();
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort.cc
new file mode 100644
index 00000000000..a9c9f95a470
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort.cc
@@ -0,0 +1,128 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static int
+db_put (DB *db, DB_TXN *txn, int k, int v) {
+ DBT key, val;
+ return db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), DB_NOOVERWRITE);
+}
+
+static const char *db_error(int error) {
+ static char errorbuf[32];
+ switch (error) {
+ case DB_NOTFOUND: return "DB_NOTFOUND";
+ case DB_LOCK_DEADLOCK: return "DB_LOCK_DEADLOCK";
+ case DB_LOCK_NOTGRANTED: return "DB_LOCK_NOTGRANTED";
+ case DB_KEYEXIST: return "DB_KEYEXIST";
+ default:
+ sprintf(errorbuf, "%d", error);
+ return errorbuf;
+ }
+}
+
+static void
+test_txn_nested(int do_commit) {
+ if (verbose) printf("test_txn_nested:%d\n", do_commit);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *db;
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.txn.nested.abort.ft_handle";
+
+ /* create the dup database file */
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK|DB_INIT_LOG |DB_THREAD |DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,stderr); // Turn off those annoying errors
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE+DB_AUTO_COMMIT, 0666); assert(r == 0);
+
+ DB_TXN *t1;
+ r = env->txn_begin(env, null_txn, &t1, 0); assert(r == 0);
+ if (verbose) printf("t1:begin\n");
+
+ DB_TXN *t2;
+ r = env->txn_begin(env, t1, &t2, 0); assert(r == 0);
+ if (verbose) printf("t2:begin\n");
+
+ r = db_put(db, t2, htonl(1), htonl(1));
+ if (verbose) printf("t1:put:%s\n", db_error(r));
+
+ if (do_commit) {
+ r = t2->commit(t2, 0);
+ if (verbose) printf("t2:commit:%s\n", db_error(r));
+ } else {
+ r = t2->abort(t2);
+ if (verbose) printf("t2:abort:%s\n", db_error(r));
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+
+ r = t1->commit(t1, 0);
+ if (verbose) printf("t1:commit:%s\n", db_error(r));
+
+ r = env->close(env, 0); assert(r == 0);
+}
+
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ test_txn_nested(0);
+ test_txn_nested(1);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort2.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort2.cc
new file mode 100644
index 00000000000..8e0105fc86e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort2.cc
@@ -0,0 +1,117 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+static void
+test_txn_abort (void) {
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ int i;
+ DB_ENV *env;
+ DBT key, val;
+ DB_TXN* txn_all = NULL;
+ DB_TXN* txn_stmt = NULL;
+ DB_TXN* txn_sp = NULL;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+
+ r = env->txn_begin(env, 0, &txn_all, 0); CKERR(r);
+
+ r = env->txn_begin(env, txn_all, &txn_stmt, 0); CKERR(r);
+ i = 1;
+ r = db->put(db, txn_stmt, dbt_init(&key, &i, sizeof i), dbt_init(&val, &i, sizeof i), 0);
+ CKERR(r);
+ r = txn_stmt->commit(txn_stmt,DB_TXN_NOSYNC);
+ txn_stmt = NULL;
+
+ r = env->txn_begin(env, txn_all, &txn_sp, 0); CKERR(r);
+
+ r = env->txn_begin(env, txn_sp, &txn_stmt, 0); CKERR(r);
+ i = 2;
+ r = db->put(db, txn_stmt, dbt_init(&key, &i, sizeof i), dbt_init(&val, &i, sizeof i), 0);
+ CKERR(r);
+ r = txn_stmt->commit(txn_stmt,DB_TXN_NOSYNC);
+ txn_stmt = NULL;
+
+
+ r = txn_all->abort(txn_all);
+ CKERR(r);
+
+
+ /* walk the db, should be empty */
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ memset(&key, 0, sizeof key);
+ memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_FIRST);
+ CKERR2(r, DB_NOTFOUND);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_txn_abort();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort3.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort3.cc
new file mode 100644
index 00000000000..4da6964a95f
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort3.cc
@@ -0,0 +1,123 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+static void
+test_txn_abort (void) {
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ int i;
+ DB_ENV *env;
+ DBT key, val;
+ DB_TXN* txn_all = NULL;
+ DB_TXN* txn_stmt = NULL;
+ DB_TXN* txn_sp = NULL;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB *db = NULL;
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+
+
+ r = env->txn_begin(env, 0, &txn_all, 0); CKERR(r);
+
+ r = env->txn_begin(env, txn_all, &txn_stmt, 0); CKERR(r);
+ i = 1;
+ r = db->put(db, txn_stmt, dbt_init(&key, &i, sizeof i), dbt_init(&val, &i, sizeof i), 0);
+ CKERR(r);
+ r = txn_stmt->commit(txn_stmt,DB_TXN_NOSYNC);
+ txn_stmt = NULL;
+
+ r = env->txn_begin(env, txn_all, &txn_sp, 0); CKERR(r);
+
+ r = env->txn_begin(env, txn_sp, &txn_stmt, 0); CKERR(r);
+ r = db->del(db, txn_stmt, dbt_init(&key, &i, sizeof i), 0);
+ CKERR(r);
+
+
+ r = txn_stmt->commit(txn_stmt,DB_TXN_NOSYNC);
+ txn_stmt = NULL;
+
+
+ r = txn_all->abort(txn_all);
+ CKERR(r);
+
+
+ {
+ /* walk the db, should be empty */
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ memset(&key, 0, sizeof key);
+ memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_FIRST);
+ CKERR2(r, DB_NOTFOUND);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0);
+ }
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_txn_abort();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort4.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort4.cc
new file mode 100644
index 00000000000..51e858fbaf6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_nested_abort4.cc
@@ -0,0 +1,148 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+static DB *db;
+static DB_ENV *env;
+
+static void
+setup_db (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ {
+ DB_TXN *txn = 0;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+ }
+}
+
+#if 0
+static void
+close_db (void) {
+ int r;
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+#endif
+
+static void
+test_txn_abort (int insert, int secondnum) {
+ if (verbose) { fprintf(stderr, "%s (%s):%d [%d,%d]\n", __FILE__, __FUNCTION__, __LINE__, insert, secondnum); fflush(stderr); }
+ setup_db();
+
+ DBT key, val;
+ int r;
+
+
+ DB_TXN *parent = NULL, *child = NULL;
+
+ int i = 1;
+ r = env->txn_begin(env, 0, &parent, 0); CKERR(r);
+
+ //Insert something as a child
+ r = env->txn_begin(env, parent, &child, 0); CKERR(r);
+ i = 1;
+ r = db->put(db, child, dbt_init(&key, &i, sizeof i), dbt_init(&val, &i, sizeof i), 0);
+ CKERR(r);
+ r = child->commit(child,DB_TXN_NOSYNC);
+ child = NULL;
+
+
+ //delete it as a child
+ r = env->txn_begin(env, parent, &child, 0); CKERR(r);
+ i = secondnum;
+ if (insert) {
+ r = db->put(db, child, dbt_init(&key, &i, sizeof i), dbt_init(&val, &i, sizeof i), 0);
+ CKERR(r);
+ }
+ else { // delete
+ r = db->del(db, child, dbt_init(&key, &i, sizeof i), DB_DELETE_ANY);
+ CKERR(r);
+ }
+ r = child->commit(child,DB_TXN_NOSYNC);
+ child = NULL;
+
+ r = parent->abort(parent);
+ CKERR(r);
+ parent = NULL;
+
+
+ {
+ DB_TXN *txn = NULL;
+ /* walk the db, should be empty */
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DBC *cursor;
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ memset(&key, 0, sizeof key);
+ memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_FIRST);
+ CKERR2(r, DB_NOTFOUND);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn->commit(txn, 0);
+ }
+ r=db->close(db, 0); CKERR(r);
+ r=env->close(env, 0); CKERR(r);
+
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ test_txn_abort(1, 0);
+ test_txn_abort(0, 0);
+ test_txn_abort(1, 1);
+ test_txn_abort(0, 1);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_read_committed_always.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_read_committed_always.cc
new file mode 100644
index 00000000000..42e0e8cd0cc
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_read_committed_always.cc
@@ -0,0 +1,121 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/**
+ * Test that read committed always isolation works.
+ *
+ * Read committed means 'always read the outermost committed value'. This is less isolated
+ * than 'read committed', which MySQl defines as 'snapshot isolation per sub-statement (child txn)'
+ */
+
+#include <portability/toku_random.h>
+
+#include "test.h"
+
+static void test_simple_committed_read(DB_ENV *env) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->open(db, NULL, "db", NULL, DB_BTREE, DB_CREATE, 0644); CKERR(r);
+
+ char valbuf[64];
+ DBT john, christian, val;
+ dbt_init(&john, "john", sizeof("john"));
+ dbt_init(&christian, "christian", sizeof("christian"));
+ dbt_init(&val, valbuf, sizeof(valbuf));
+
+ // start with just john
+ r = db->put(db, NULL, &john, &john, 0); CKERR(r);
+
+ // begin an outer txn with read-committed-always isolation
+ DB_TXN *outer_txn;
+ r = env->txn_begin(env, NULL, &outer_txn, DB_READ_COMMITTED_ALWAYS); CKERR(r);
+
+ // outer txn sees john
+ r = db->get(db, outer_txn, &john, &val, 0); CKERR(r);
+
+ // outer txn does not yet see christian
+ r = db->get(db, outer_txn, &christian, &val, 0); CKERR2(r, DB_NOTFOUND);
+
+ // insert christian in another txn (NULL means generate an auto-commit txn)
+ r = db->put(db, NULL, &christian, &christian, 0); CKERR(r);
+
+ // outer txn does not see christian, because it is provisional
+ // and our copied snapshot says it is not committed
+ r = db->get(db, outer_txn, &christian, &val, 0); CKERR2(r, DB_NOTFOUND);
+
+ // insert christian in another txn (again), thereby autocommitting last put
+ r = db->put(db, NULL, &christian, &christian, 0); CKERR(r);
+
+ // outer txn sees christian, because we now have a committed version
+ r = db->get(db, outer_txn, &christian, &val, 0); CKERR(r);
+
+ // delete john in another txn
+ r = db->del(db, NULL, &john, 0); CKERR(r);
+
+ // outer txn no longer sees john
+ r = db->get(db, outer_txn, &john, &val, 0); CKERR2(r, DB_NOTFOUND);
+
+ r = outer_txn->commit(outer_txn, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->dbremove(env, NULL, "db", NULL, 0); CKERR(r);
+}
+
+int test_main(int argc, char * const argv[]) {
+ default_parse_args(argc, argv);
+
+ int r;
+ const int envflags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD |
+ DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE;
+
+ // startup
+ DB_ENV *env;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, 0755); CKERR(r);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME, envflags, 0755);
+
+ test_simple_committed_read(env);
+
+ // cleanup
+ r = env->close(env, 0); CKERR(r);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/test_txn_recover3.cc b/storage/tokudb/PerconaFT/src/tests/test_txn_recover3.cc
new file mode 100644
index 00000000000..46124e17cb7
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_txn_recover3.cc
@@ -0,0 +1,140 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+test_txn_recover3 (int nrows) {
+ if (verbose) printf("test_txn_recover1:%d\n", nrows);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ char dirname[TOKU_PATH_MAX+1];
+ toku_os_mkdir(toku_path_join(dirname, 2, TOKU_TEST_FILENAME, "t.tokudb"), S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env;
+ DB *mdb, *sdb;
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "t.tokudb/main.ft_handle";
+ const char * const sname = "t.tokudb/status.ft_handle";
+
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK|DB_INIT_LOG |DB_THREAD |DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK|DB_INIT_LOG |DB_THREAD |DB_PRIVATE | DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&mdb, env, 0); assert(r == 0);
+ mdb->set_errfile(mdb,stderr); // Turn off those annoying errors
+ r = mdb->open(mdb, null_txn, fname, NULL, DB_BTREE, DB_CREATE+DB_THREAD+DB_AUTO_COMMIT, 0666); assert(r == 0);
+ r = mdb->close(mdb, 0); assert(r == 0);
+
+ r = db_create(&sdb, env, 0); assert(r == 0);
+ sdb->set_errfile(sdb,stderr); // Turn off those annoying errors
+ r = sdb->open(sdb, null_txn, sname, NULL, DB_BTREE, DB_CREATE+DB_THREAD+DB_AUTO_COMMIT, 0666); assert(r == 0);
+ r = sdb->close(sdb, 0); assert(r == 0);
+
+ r = db_create(&mdb, env, 0); assert(r == 0);
+ mdb->set_errfile(mdb,stderr); // Turn off those annoying errors
+ r = mdb->open(mdb, null_txn, fname, NULL, DB_BTREE, DB_CREATE+DB_THREAD+DB_AUTO_COMMIT, 0666); assert(r == 0);
+
+ r = db_create(&sdb, env, 0); assert(r == 0);
+ sdb->set_errfile(sdb,stderr); // Turn off those annoying errors
+ r = sdb->open(sdb, null_txn, sname, NULL, DB_BTREE, DB_CREATE+DB_THREAD+DB_AUTO_COMMIT, 0666); assert(r == 0);
+
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, null_txn, &txn, 0); assert(r == 0);
+
+ int i;
+ for (i=0; i<nrows; i++) {
+ int k = htonl(i);
+ int v = htonl(i);
+ DBT key, val;
+ r = mdb->put(mdb, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+ r = sdb->put(sdb, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ assert(r == 0);
+ }
+
+ r = txn->commit(txn, 0); assert(r == 0);
+
+ r = mdb->close(mdb, 0); assert(r == 0);
+ r = sdb->close(sdb, 0); assert(r == 0);
+
+ r = env->txn_checkpoint(env, 0, 0, 0); assert(r == 0);
+
+ char **names;
+ r = env->log_archive(env, &names, 0); assert(r == 0);
+ if (names) {
+ for (i=0; names[i]; i++)
+ printf("%d:%s\n", i, names[i]);
+ toku_free(names);
+ }
+
+ r = env->close(env, 0); assert(r == 0);
+
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE|DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOCK|DB_INIT_LOG |DB_THREAD |DB_PRIVATE | DB_RECOVER, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ test_txn_recover3(1);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_unused_memory_crash.cc b/storage/tokudb/PerconaFT/src/tests/test_unused_memory_crash.cc
new file mode 100644
index 00000000000..f273a02a791
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_unused_memory_crash.cc
@@ -0,0 +1,137 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+static void
+expect_cursor_get (DBC *cursor, int k, int v, int op) {
+ int kk, vv;
+ DBT key, val;
+ int r = cursor->c_get(cursor, dbt_init_malloc(&key), dbt_init_malloc(&val), op);
+ assert(r == 0);
+ assert(key.size == sizeof kk); memcpy(&kk, key.data, key.size); assert(kk == k); toku_free(key.data);
+ assert(val.size == sizeof vv); memcpy(&vv, val.data, val.size); assert(vv == v); toku_free(val.data);
+}
+
+static DBC *
+new_cursor (DB *db, int k, int v, int op) {
+ DBC *cursor;
+ int r;
+ r = db->cursor(db, 0, &cursor, 0); assert(r == 0);
+ expect_cursor_get(cursor, k, v, op);
+ return cursor;
+}
+
+static int
+db_put (DB *db, int k, int v) {
+ DBT key, val;
+ int r = db->put(db, 0, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), 0);
+ return r;
+}
+
+static void
+test_cursor_nonleaf_expand (int n, int reverse) {
+ if (verbose) printf("test_cursor_nonleaf_expand:%d %d\n", n, reverse);
+
+ DB_TXN * const null_txn = 0;
+ const char * const fname = "test.insert.ft_handle";
+ int r;
+
+ // toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ // r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_pagesize(db, 4096); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ r = db_put(db, htonl(0), 0); assert(r == 0);
+ DBC *cursor0 = new_cursor(db, htonl(0), 0, DB_FIRST); assert(cursor0);
+ r = db_put(db, htonl(n), n); assert(r == 0);
+ DBC *cursorn = new_cursor(db, htonl(n), n, DB_LAST); assert(cursorn);
+
+ int i;
+ if (reverse) {
+ for (i=n-1; i > 0; i--) {
+ r = db_put(db, htonl(i), i); assert(r == 0);
+ }
+ } else {
+ for (i=1; i < n; i++) {
+ r = db_put(db, htonl(i), i); assert(r == 0);
+ }
+ }
+
+ expect_cursor_get(cursor0, htonl(0), 0, DB_CURRENT);
+ expect_cursor_get(cursorn, htonl(n), n, DB_CURRENT);
+
+ r = cursor0->c_close(cursor0); assert(r == 0);
+ r = cursorn->c_close(cursorn); assert(r == 0);
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+ parse_args(argc, argv);
+
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ int i;
+ for (i=1; i<=65536; i *= 2) {
+ test_cursor_nonleaf_expand(i, 0);
+ test_cursor_nonleaf_expand(i, 1);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_abort_works.cc b/storage/tokudb/PerconaFT/src/tests/test_update_abort_works.cc
new file mode 100644
index 00000000000..70194ea42c9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_abort_works.cc
@@ -0,0 +1,191 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an aborted update doesn't affect later reads
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ if (to_update[i] == 1) {
+ e = _e(i); // E I O
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void chk_updated(const unsigned int k, const unsigned int v) {
+ if (to_update[k]) {
+ assert(v == _u(_v(k), _e(k)));
+ } else {
+ assert(v == _v(k));
+ }
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v)) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ return r;
+}
+
+
+static void run_test(bool do_close) {
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_2, txn_21, 0, {
+ { int chk_r = do_verify_results(txn_21, db, chk_updated); CKERR(chk_r); }
+ });
+ });
+
+ if (do_close) {
+ { int chk_r = db->close(db,0); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ }
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_original); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+}
+
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ run_test(false);
+ run_test(true);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_abort_works.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_abort_works.cc
new file mode 100644
index 00000000000..0d538844cf6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_abort_works.cc
@@ -0,0 +1,182 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that aborting an update broadcast works correctly
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = 100;
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ e = _e(*k);
+ v = _u(*ov, e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db, uint32_t flags) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, flags); CKERR(r);
+ return r;
+}
+
+static void chk_updated(const unsigned int k, const unsigned int v) {
+ assert(v == _u(_v(k), _e(k)));
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v)) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ return r;
+}
+
+static void run_test(bool is_resetting) {
+ DB *db;
+ uint32_t update_flags = is_resetting ? DB_IS_RESETTING_OP : 0;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, update_flags); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_2, txn_21, 0, {
+ { int chk_r = do_verify_results(txn_21, db, chk_updated); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_original); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_4, 0, {
+ { int chk_r = do_updates(txn_4, db, update_flags); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_4, txn_41, 0, {
+ { int chk_r = do_verify_results(txn_41, db, chk_updated); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_5, 0, {
+ { int chk_r = do_verify_results(txn_5, db, chk_updated); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test(true);
+ run_test(false);
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_calls_back.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_calls_back.cc
new file mode 100644
index 00000000000..468747a15c0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_calls_back.cc
@@ -0,0 +1,136 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+int updates_called[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+// the commands are: byte 1 is "nop" "add" or "del". Byte 2 is the amount to add.
+enum cmd { CNOP, CADD, CDEL };
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *UU(old_val), const DBT *UU(extra),
+ void UU((*set_val)(const DBT *new_val,
+ void *set_extra)),
+ void *UU(set_extra)) {
+ unsigned int *k;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(updates_called[*k] == 0);
+ updates_called[*k] = 1;
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(bool is_resetting) {
+ DB *db;
+ uint32_t update_flags = is_resetting ? DB_IS_RESETTING_OP : 0;
+ for (unsigned int i = 0;
+ i < (sizeof(updates_called) / sizeof(updates_called[0])); ++i) {
+ updates_called[i] = 0;
+ }
+
+ {
+ DB_TXN* txna = NULL;
+ { int chk_r = env->txn_begin(env, NULL, &txna, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ {
+ DBT key, val;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, "a", 2);
+ for (i = 0; i < (sizeof(updates_called) / sizeof(updates_called[0])); ++i) {
+ { int chk_r = db->put(db, txna, keyp, valp, 0); CKERR(chk_r); }
+ }
+ }
+
+ { int chk_r = txna->commit(txna, 0); CKERR(chk_r); }
+ }
+
+ {
+ DB_TXN *txnb = NULL;
+ { int chk_r = env->txn_begin(env, NULL, &txnb, 0); CKERR(chk_r); }
+
+ {
+ DBT nullextra;
+ DBT *nullextrap = dbt_init(&nullextra, NULL, 0);
+ { int chk_r = db->update_broadcast(db, txnb, nullextrap, update_flags); CKERR(chk_r); }
+ }
+
+ { int chk_r = txnb->commit(txnb, 0); CKERR(chk_r); }
+ }
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ for (unsigned int i = 0;
+ i < (sizeof(updates_called) / sizeof(updates_called[0])); ++i) {
+ assert(updates_called[i]);
+ }
+
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test(true);
+ run_test(false);
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_can_delete_elements.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_can_delete_elements.cc
new file mode 100644
index 00000000000..76fbbbfcbdf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_can_delete_elements.cc
@@ -0,0 +1,166 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that update broadcast can delete (all) elements
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = 100;
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+
+ set_val(NULL, set_extra);
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db, uint32_t flags) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, flags); CKERR(r);
+ return r;
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v), bool already_deleted) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn, keyp, valp, 0);
+ if (already_deleted) {
+ CKERR2(r, DB_NOTFOUND);
+ r = 0;
+ } else {
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ }
+ return r;
+}
+
+static void run_test(bool is_resetting) {
+ DB *db;
+ uint32_t update_flags = is_resetting ? DB_IS_RESETTING_OP : 0;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original, false); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, update_flags); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_2, txn_21, 0, {
+ { int chk_r = do_verify_results(txn_21, db, chk_original, true); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_original, false); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ }
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test(true);
+ run_test(false);
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_changes_values.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_changes_values.cc
new file mode 100644
index 00000000000..130f023a154
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_changes_values.cc
@@ -0,0 +1,154 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update broadcast can change (all) values
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = 100;
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ e = _e(*k);
+ v = _u(*ov, e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db, uint32_t flags) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, flags); CKERR(r);
+ return r;
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ assert(*vp == _u(_v(i), _e(i)));
+ }
+ return r;
+}
+
+static void run_test(bool is_resetting) {
+ DB *db;
+ uint32_t update_flags = is_resetting ? DB_IS_RESETTING_OP : 0;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, update_flags); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test(true);
+ run_test(false);
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_indexer.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_indexer.cc
new file mode 100644
index 00000000000..1ee699e1fdb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_indexer.cc
@@ -0,0 +1,225 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *UU(old_val), const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ set_val(extra, set_extra);
+ return 0;
+}
+
+
+static int generate_row_for_del(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *dest_key_arrays,
+ const DBT *UU(src_key),
+ const DBT *UU(src_val)
+ )
+{
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ dest_key->size=0;
+ return 0;
+}
+
+static int generate_row_for_put(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *dest_key_arrays,
+ DBT_ARRAY *dest_val_arrays,
+ const DBT *UU(src_key),
+ const DBT *UU(src_val)
+ )
+{
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+
+ uint8_t src_val_data;
+ assert(src_val->size == 1);
+ src_val_data = *(uint8_t *)src_val->data;
+ assert(src_val_data == 100);
+ dest_key->size=0;
+ dest_val->size=0;
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->set_generate_row_callback_for_put(env,generate_row_for_put); CKERR(chk_r); }
+ { int chk_r = env->set_generate_row_callback_for_del(env,generate_row_for_del); CKERR(chk_r); }
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+ DB* hot_index_db = NULL;
+ DB_INDEXER* indexer = NULL;
+ DBT key, val;
+ uint32_t mult_db_flags = 0;
+ uint8_t key_data = 0;
+ uint8_t val_data = 0;
+ DB_TXN* txn_read1 = NULL;
+ DB_TXN* txn_read2 = NULL;
+ DB_TXN* txn_read3 = NULL;
+
+
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+
+
+ dbt_init(&key,&key_data,sizeof(uint8_t));
+ dbt_init(&val,&val_data,sizeof(uint8_t));
+
+ val_data = 1;
+ IN_TXN_COMMIT(env, NULL, txn_put1, 0, {
+ { int chk_r = db->put(db, txn_put1, &key, &val, 0); CKERR(chk_r); }
+ });
+ { int chk_r = env->txn_begin(env, NULL, &txn_read1, DB_TXN_SNAPSHOT); CKERR(chk_r); }
+
+ val_data = 2;
+ IN_TXN_COMMIT(env, NULL, txn_put2, 0, {
+ { int chk_r = db->put(db, txn_put2, &key, &val, 0); CKERR(chk_r); }
+ });
+ { int chk_r = env->txn_begin(env, NULL, &txn_read2, DB_TXN_SNAPSHOT); CKERR(chk_r); }
+
+ val_data = 3;
+ IN_TXN_COMMIT(env, NULL, txn_put3, 0, {
+ { int chk_r = db->put(db, txn_put3, &key, &val, 0); CKERR(chk_r); }
+ });
+ { int chk_r = env->txn_begin(env, NULL, &txn_read3, DB_TXN_SNAPSHOT); CKERR(chk_r); }
+
+ //
+ // at this point, we should have a leafentry with 3 committed values.
+ //
+
+
+ //
+ // now do an update broadcast that will set the val to something bigger
+ //
+ val_data = 100;
+ IN_TXN_COMMIT(env, NULL, txn_broadcast, 0, {
+ { int chk_r = db->update_broadcast(db, txn_broadcast, &val, DB_IS_RESETTING_OP); CKERR(chk_r); }
+ });
+
+ //
+ // now create an indexer
+ //
+ IN_TXN_COMMIT(env, NULL, txn_indexer, 0, {
+ // create DB
+ { int chk_r = db_create(&hot_index_db, env, 0); CKERR(chk_r); }
+ { int chk_r = hot_index_db->open(hot_index_db, txn_indexer, "bar.db", NULL, DB_BTREE, DB_CREATE|DB_IS_HOT_INDEX, 0666); CKERR(chk_r); }
+ { int chk_r = env->create_indexer(
+ env,
+ txn_indexer,
+ &indexer,
+ db,
+ 1,
+ &hot_index_db,
+ &mult_db_flags,
+ 0
+ ); CKERR(chk_r); }
+ { int chk_r = indexer->build(indexer); CKERR(chk_r); }
+ { int chk_r = indexer->close(indexer); CKERR(chk_r); }
+ });
+
+ //verify that txn_read1,2,3 cannot open a cursor on db
+ DBC* cursor = NULL;
+ { int chk_r = db->cursor(db, txn_read1, &cursor, 0); CKERR2(chk_r, TOKUDB_MVCC_DICTIONARY_TOO_NEW); }
+ { int chk_r = db->cursor(db, txn_read2, &cursor, 0); CKERR2(chk_r, TOKUDB_MVCC_DICTIONARY_TOO_NEW); }
+ { int chk_r = db->cursor(db, txn_read3, &cursor, 0); CKERR2(chk_r, TOKUDB_MVCC_DICTIONARY_TOO_NEW); }
+ IN_TXN_COMMIT(env, NULL, txn_read_succ, 0, {
+ { int chk_r = db->cursor(db, txn_read_succ, &cursor, 0); CKERR(chk_r); }
+ { int chk_r = cursor->c_close(cursor); CKERR(chk_r); }
+ cursor = NULL;
+ });
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, NULL, "foo.db", NULL, DB_BTREE, 0, 0666); CKERR(chk_r); }
+ { int chk_r = db->cursor(db, txn_read1, &cursor, 0); CKERR2(chk_r, TOKUDB_MVCC_DICTIONARY_TOO_NEW); }
+ { int chk_r = db->cursor(db, txn_read2, &cursor, 0); CKERR2(chk_r, TOKUDB_MVCC_DICTIONARY_TOO_NEW); }
+ { int chk_r = db->cursor(db, txn_read3, &cursor, 0); CKERR2(chk_r, TOKUDB_MVCC_DICTIONARY_TOO_NEW); }
+ IN_TXN_COMMIT(env, NULL, txn_read_succ, 0, {
+ { int chk_r = db->cursor(db, txn_read_succ, &cursor, 0); CKERR(chk_r); }
+ { int chk_r = cursor->c_close(cursor); CKERR(chk_r); }
+ cursor = NULL;
+ });
+
+ // commit the read transactions
+ { int chk_r = txn_read1->commit(txn_read1, 0); CKERR(chk_r); }
+ { int chk_r = txn_read2->commit(txn_read2, 0); CKERR(chk_r); }
+ { int chk_r = txn_read3->commit(txn_read3, 0); CKERR(chk_r); }
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ { int chk_r = hot_index_db->close(hot_index_db, 0); CKERR(chk_r); }
+
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_loader.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_loader.cc
new file mode 100644
index 00000000000..ea80be6a1e2
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_loader.cc
@@ -0,0 +1,178 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *UU(old_val), const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ set_val(extra, set_extra);
+ return 0;
+}
+
+
+static int generate_row_for_del(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *dest_key_arrays,
+ const DBT *UU(src_key),
+ const DBT *UU(src_val)
+ )
+{
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ dest_key->flags = 0;
+ dest_key->size=0;
+ return 0;
+}
+
+static int generate_row_for_put(
+ DB *UU(dest_db),
+ DB *UU(src_db),
+ DBT_ARRAY *dest_key_arrays,
+ DBT_ARRAY *dest_val_arrays,
+ const DBT *UU(src_key),
+ const DBT *UU(src_val)
+ )
+{
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ dest_key->flags = 0;
+ dest_val->flags = 0;
+
+ uint8_t src_val_data;
+ assert(src_val->size == 1);
+ src_val_data = *(uint8_t *)src_val->data;
+ assert(src_val_data == 100);
+ dest_key->size=0;
+ dest_val->size=0;
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ { int chk_r = env->set_generate_row_callback_for_put(env,generate_row_for_put); CKERR(chk_r); }
+ { int chk_r = env->set_generate_row_callback_for_del(env,generate_row_for_del); CKERR(chk_r); }
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static void run_test(void) {
+ DB* db = NULL;
+ DB_LOADER* loader = NULL;
+ DBT key, val;
+ uint32_t mult_db_flags = 0;
+ uint32_t mult_dbt_flags = DB_DBT_REALLOC;
+ uint8_t key_data = 0;
+ uint8_t val_data = 0;
+
+
+ IN_TXN_COMMIT(env, NULL, txn_create, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_create, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+
+
+ dbt_init(&key,&key_data,sizeof(uint8_t));
+ dbt_init(&val,&val_data,sizeof(uint8_t));
+
+ val_data = 1;
+
+
+ //
+ // now do an update broadcast that will set the val to something bigger
+ //
+ val_data = 100;
+ IN_TXN_COMMIT(env, NULL, txn_broadcast, 0, {
+ { int chk_r = db->update_broadcast(db, txn_broadcast, &val, DB_IS_RESETTING_OP); CKERR(chk_r); }
+ });
+
+ //
+ // now create a loader
+ //
+ IN_TXN_COMMIT(env, NULL, txn_loader, 0, {
+ // create DB
+ { int chk_r = env->create_loader(
+ env,
+ txn_loader,
+ &loader,
+ db,
+ 1,
+ &db,
+ &mult_db_flags,
+ &mult_dbt_flags,
+ 0
+ ); CKERR(chk_r); }
+ { int chk_r = loader->put(loader, &key, &val); CKERR(chk_r); }
+ { int chk_r = loader->close(loader); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_update, 0, {
+ { int chk_r = db->update(db, txn_update, &key, &val, 0); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_nested_updates.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_nested_updates.cc
new file mode 100644
index 00000000000..113832ff459
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_nested_updates.cc
@@ -0,0 +1,165 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update broadcast can change (all) values
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = 100;
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ e = _e(*k);
+ v = _u(*ov, e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db, uint32_t flags) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, flags);
+ return r;
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, bool updated_twice) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (updated_twice) {
+ assert(*vp == _u(_u(_v(i), _e(i)), _e(i)));
+ } else {
+ assert(*vp == _u(_v(i), _e(i)));
+ }
+ }
+ return r;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, 0); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_2, db, false); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_2, txn_21, 0, {
+ { int chk_r = do_updates(txn_21, db, 0); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_21, db, true); CKERR(chk_r); }
+ });
+ IN_TXN_COMMIT(env, txn_2, txn_22, 0, {
+ { int chk_r = do_updates(txn_22, db, DB_IS_RESETTING_OP); CKERR2(chk_r, EINVAL); }
+ });
+
+ { int chk_r = do_verify_results(txn_2, db, true); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, true); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_previously_deleted.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_previously_deleted.cc
new file mode 100644
index 00000000000..c6d03427385
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_previously_deleted.cc
@@ -0,0 +1,196 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update broadcast called when there are previously deleted
+// keys doesn't get a callback to update_fun on those keys
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_delete[] = { 1, 1, 0, 0, 1, 0, 0, 0, 1, 0 };
+const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(extra->size == 0);
+ if (to_delete[*k]) {
+ assert(0);
+ } else {
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ v = _u(*ov, _e(*k));
+ }
+
+ if (to_update[*k]) {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_deletes(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ for (i = 0; i < (sizeof(to_delete) / sizeof(to_delete[0])); ++i) {
+ if (to_delete[i]) {
+ r = db->del(db, txn, keyp, DB_DELETE_ANY); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db, uint32_t flags) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, flags); CKERR(r);
+ return r;
+}
+
+static void chk_updated(const unsigned int k, const unsigned int v) {
+ if (to_update[k]) {
+ assert(v == _u(_v(k), _e(k)));
+ } else {
+ assert(v == _v(k));
+ }
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v)) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0);
+ if (to_delete[i]) {
+ CKERR2(r, DB_NOTFOUND);
+ r = 0;
+ } else {
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ }
+ return r;
+}
+
+static void run_test(bool is_resetting) {
+ DB *db;
+ uint32_t update_flags = is_resetting ? DB_IS_RESETTING_OP : 0;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ { int chk_r = do_deletes(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, update_flags); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_updated); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test(true);
+ run_test(false);
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_stress.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_stress.cc
new file mode 100644
index 00000000000..eebbd7fc3ab
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_stress.cc
@@ -0,0 +1,177 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// stress test for update broadcast. 10M 8-byte keys should be 2, maybe 3
+// levels of treeness, makes sure flushes work
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = 10000000;
+const unsigned int MAGIC_EXTRA = 0x4ac0ffee;
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v + 2 * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ assert(*e == MAGIC_EXTRA);
+ v = _u(*ov, _e(*k));
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int
+int_cmp(DB *UU(db), const DBT *a, const DBT *b) {
+ unsigned int *ap, *bp;
+ assert(a->size == sizeof(*ap));
+ CAST_FROM_VOIDP(ap, a->data);
+ assert(b->size == sizeof(*bp));
+ CAST_FROM_VOIDP(bp, b->data);
+ return (*ap > *bp) - (*ap < *bp);
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->set_default_bt_compare(env, int_cmp); CKERR(chk_r); }
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ DBT extra;
+ unsigned int e = MAGIC_EXTRA;
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ int r = db->update_broadcast(db, txn, extrap, 0); CKERR(r);
+ return r;
+}
+
+static void chk_updated(const unsigned int k, const unsigned int v) {
+ assert(v == _u(_v(k), _e(k)));
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v)) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ return r;
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_2, txn_21, 0, {
+ { int chk_r = do_verify_results(txn_21, db, chk_updated); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_original); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_update_fun_has_choices.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_update_fun_has_choices.cc
new file mode 100644
index 00000000000..24ed5210ed9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_update_fun_has_choices.cc
@@ -0,0 +1,176 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update broadcast can change and delete different values,
+// or do nothing
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = 1000;
+
+static inline bool should_insert(const unsigned int i) { return i % 2 == 0; }
+static inline bool should_update(const unsigned int i) { return i % 3 == 0; }
+static inline bool should_delete(const unsigned int i) { return (i % 5 == 0) && (i % 3 != 0); }
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(should_insert(*k));
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+ if (should_update(*k)) {
+ e = _e(*k);
+ v = _u(*ov, e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+ } else if (should_delete(*k)) {
+ set_val(NULL, set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ if (should_insert(i)) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db, uint32_t flags) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, flags); CKERR(r);
+ return r;
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn, keyp, valp, 0);
+ if (!should_insert(i) || should_delete(i)) {
+ CKERR2(r, DB_NOTFOUND);
+ r = 0;
+ } else if (should_insert(i)) {
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (should_update(i)) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ }
+ return r;
+}
+
+static void run_test(bool is_resetting) {
+ DB *db;
+ uint32_t update_flags = is_resetting ? DB_IS_RESETTING_OP : 0;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, update_flags); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test(true);
+ run_test(false);
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_with_empty_table.cc b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_with_empty_table.cc
new file mode 100644
index 00000000000..e46b5398fb0
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_broadcast_with_empty_table.cc
@@ -0,0 +1,107 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that update broadcast does nothing if the table is empty
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *UU(old_val), const DBT *UU(extra),
+ void UU((*set_val)(const DBT *new_val,
+ void *set_extra)),
+ void *UU(set_extra)) {
+ assert(0); return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_updates(DB_TXN *txn, DB *db, uint32_t flags) {
+ DBT extra;
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ int r = db->update_broadcast(db, txn, extrap, flags); CKERR(r);
+ return r;
+}
+
+static void run_test(bool is_resetting, bool prelock) {
+ DB *db;
+ uint32_t update_flags = is_resetting ? DB_IS_RESETTING_OP : 0;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ if (prelock) {
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db->pre_acquire_table_lock(db, txn_2); CKERR(chk_r); }
+ });
+ }
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, update_flags); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ run_test(true,true);
+ run_test(false,true);
+ run_test(true,false);
+ run_test(false,false);
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_calls_back.cc b/storage/tokudb/PerconaFT/src/tests/test_update_calls_back.cc
new file mode 100644
index 00000000000..57664062f33
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_calls_back.cc
@@ -0,0 +1,136 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update calls back into the update function
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+ int updates_called[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+// the commands are: byte 1 is "nop" "add" or "del". Byte 2 is the amount to add.
+enum cmd { CNOP, CADD, CDEL };
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *UU(old_val), const DBT *UU(extra),
+ void UU((*set_val)(const DBT *new_val,
+ void *set_extra)),
+ void *UU(set_extra)) {
+ unsigned int *k;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(to_update[*k] == 1);
+ assert(updates_called[*k] == 0);
+ updates_called[*k] = 1;
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ {
+ DB_TXN* txna = NULL;
+ { int chk_r = env->txn_begin(env, NULL, &txna, 0); CKERR(chk_r); }
+
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txna, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ {
+ DBT key, val;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, "a", 2);
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ { int chk_r = db->put(db, txna, keyp, valp, 0); CKERR(chk_r); }
+ }
+ }
+
+ { int chk_r = txna->commit(txna, 0); CKERR(chk_r); }
+ }
+
+ {
+ DB_TXN *txnb = NULL;
+ { int chk_r = env->txn_begin(env, NULL, &txnb, 0); CKERR(chk_r); }
+
+ {
+ DBT key, nullextra;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *nullextrap = dbt_init(&nullextra, NULL, 0);
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ if (to_update[i] == 1) {
+ { int chk_r = db->update(db, txnb, keyp, nullextrap, 0); CKERR(chk_r); }
+ }
+ }
+ }
+
+ { int chk_r = txnb->commit(txnb, 0); CKERR(chk_r); }
+ }
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ for (unsigned int i = 0;
+ i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ assert(to_update[i] == updates_called[i]);
+ }
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_can_delete_elements.cc b/storage/tokudb/PerconaFT/src/tests/test_update_can_delete_elements.cc
new file mode 100644
index 00000000000..389aec32999
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_can_delete_elements.cc
@@ -0,0 +1,168 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update can delete some elements
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_delete[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == 0);
+
+ set_val(NULL, set_extra);
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_delete) / sizeof(to_delete[0])); ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ for (i = 0; i < (sizeof(to_delete) / sizeof(to_delete[0])); ++i) {
+ if (to_delete[i] == 1) {
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v), bool already_deleted) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < (sizeof(to_delete) / sizeof(to_delete[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0);
+ if (already_deleted && to_delete[i]) {
+ CKERR2(r, DB_NOTFOUND);
+ r = 0;
+ } else {
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ }
+ return r;
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original, false); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_2, txn_21, 0, {
+ { int chk_r = do_verify_results(txn_21, db, chk_original, true); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_original, false); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_changes_values.cc b/storage/tokudb/PerconaFT/src/tests/test_update_changes_values.cc
new file mode 100644
index 00000000000..5bcebf742f6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_changes_values.cc
@@ -0,0 +1,162 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update changes some values
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ if (to_update[i] == 1) {
+ e = _e(i); // E I O
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (to_update[i]) {
+ assert(*vp == _u(_v(i), _e(i)));
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ return r;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_nested_updates.cc b/storage/tokudb/PerconaFT/src/tests/test_update_nested_updates.cc
new file mode 100644
index 00000000000..6f1017d844d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_nested_updates.cc
@@ -0,0 +1,174 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that a transaction can update, then create a child which also updates
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ if (to_update[i] == 1) {
+ e = _e(i); // E I O
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, bool updated_twice) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ if (to_update[i]) {
+ if (updated_twice) {
+ assert(*vp == _u(_u(_v(i), _e(i)), _e(i)));
+ } else {
+ assert(*vp == _u(_v(i), _e(i)));
+ }
+ } else {
+ assert(*vp == _v(i));
+ }
+ }
+ return r;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_2, db, false); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_2, txn_21, 0, {
+ { int chk_r = do_updates(txn_21, db); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_21, db, true); CKERR(chk_r); }
+ });
+
+ { int chk_r = do_verify_results(txn_2, db, true); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, true); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_nonexistent_keys.cc b/storage/tokudb/PerconaFT/src/tests/test_update_nonexistent_keys.cc
new file mode 100644
index 00000000000..79fc0d14b80
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_nonexistent_keys.cc
@@ -0,0 +1,193 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update, if called on a nonexistent key, will call back
+// into update_function with the right arguments, and allows it to set a
+// new value
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_insert[] = { 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 };
+const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline bool should_insert(const unsigned int i) { return to_insert[i]; }
+static inline bool should_update(const unsigned int i) { return to_update[i]; }
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ if (!should_insert(*k)) {
+ assert(old_val == NULL);
+ v = _u(_v(*k), *e);
+ } else {
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ v = _u(*ov, *e);
+ }
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ if (should_insert(i)) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ const DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ const DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ if (should_update(i)) {
+ e = _e(i); // E I O
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void chk_updated(const unsigned int k, const unsigned int v) {
+ if (should_update(k)) {
+ assert(v == _u(_v(k), _e(k)));
+ } else {
+ assert(v == _v(k));
+ }
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v), bool after_update) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_insert) / sizeof(to_insert[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0);
+ if (should_insert(i) || (after_update && should_update(i))) {
+ CKERR(r);
+ assert(val.size == sizeof(v));
+ v = *(unsigned int *) val.data;
+
+ check_val(i, v);
+ } else {
+ CKERR2(r, DB_NOTFOUND);
+ r = 0;
+ }
+ }
+ return r;
+}
+
+int test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_ABORT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original, false); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_updated, true); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_previously_deleted.cc b/storage/tokudb/PerconaFT/src/tests/test_update_previously_deleted.cc
new file mode 100644
index 00000000000..11c4f536b48
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_previously_deleted.cc
@@ -0,0 +1,202 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update called on previously deleted keys works the same as
+// with nonexistent keys
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_delete[] = { 1, 1, 0, 0, 1, 0, 0, 0, 1, 0 };
+const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ if (to_delete[*k]) {
+ assert(old_val == NULL);
+ v = _u(_v(*k), *e);
+ } else {
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ v = _u(*ov, *e);
+ }
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_deletes(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ for (i = 0; i < (sizeof(to_delete) / sizeof(to_delete[0])); ++i) {
+ if (to_delete[i]) {
+ r = db->del(db, txn, keyp, DB_DELETE_ANY); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ if (to_update[i] == 1) {
+ e = _e(i); // E I O
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void chk_updated(const unsigned int k, const unsigned int v) {
+ if (to_update[k]) {
+ assert(v == _u(_v(k), _e(k)));
+ } else {
+ assert(v == _v(k));
+ }
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v), bool already_updated) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0);
+ if (to_delete[i] && !(already_updated && to_update[i])) {
+ CKERR2(r, DB_NOTFOUND);
+ r = 0;
+ } else {
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ }
+ return r;
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ { int chk_r = do_deletes(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original, false); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_updated, true); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_stress.cc b/storage/tokudb/PerconaFT/src/tests/test_update_stress.cc
new file mode 100644
index 00000000000..75b4219fce1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_stress.cc
@@ -0,0 +1,187 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// stress test for update. 10M 8-byte keys should be 2, maybe 3 levels of
+// treeness, makes sure flushes work
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const unsigned int NUM_KEYS = 10000000;
+
+static inline bool should_update(const unsigned int i) { return i % 3 == 0; }
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v + 2 * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static int
+int_cmp(DB *UU(db), const DBT *a, const DBT *b) {
+ unsigned int *ap, *bp;
+ assert(a->size == sizeof(*ap));
+ CAST_FROM_VOIDP(ap, a->data);
+ assert(b->size == sizeof(*bp));
+ CAST_FROM_VOIDP(bp, b->data);
+ return (*ap > *bp) - (*ap < *bp);
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->set_default_bt_compare(env, int_cmp); CKERR(chk_r); }
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < NUM_KEYS; ++i) {
+ if (should_update(i)) {
+ e = _e(i); // E I O
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void chk_updated(const unsigned int k, const unsigned int v) {
+ if (should_update(k)) {
+ assert(v == _u(_v(k), _e(k)));
+ } else {
+ assert(v == _v(k));
+ }
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v)) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < NUM_KEYS; ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ return r;
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+ });
+
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_2, txn_21, 0, {
+ { int chk_r = do_verify_results(txn_21, db, chk_updated); CKERR(chk_r); }
+ });
+ });
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = do_verify_results(txn_3, db, chk_original); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_txn_snapshot_works_concurrently.cc b/storage/tokudb/PerconaFT/src/tests/test_update_txn_snapshot_works_concurrently.cc
new file mode 100644
index 00000000000..1985bdaebf9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_txn_snapshot_works_concurrently.cc
@@ -0,0 +1,183 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update doesn't infringe on the values read by another txn
+// started with TXN_SNAPSHOT
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_update[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+static inline unsigned int _e(const unsigned int i) { return i + 4; }
+static inline unsigned int _u(const unsigned int v, const unsigned int e) { return v * v * e; }
+
+static int update_fun(DB *UU(db),
+ const DBT *key,
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ unsigned int *k, *ov, *e, v;
+ assert(key->size == sizeof(*k));
+ CAST_FROM_VOIDP(k, key->data);
+ assert(old_val->size == sizeof(*ov));
+ CAST_FROM_VOIDP(ov, old_val->data);
+ assert(extra->size == sizeof(*e));
+ CAST_FROM_VOIDP(e, extra->data);
+ v = _u(*ov, *e);
+
+ {
+ DBT newval;
+ set_val(dbt_init(&newval, &v, sizeof(v)), set_extra);
+ }
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i, e;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, &e, sizeof(e));
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ if (to_update[i] == 1) {
+ e = _e(i); // E I O
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void chk_updated(const unsigned int k, const unsigned int v) {
+ if (to_update[k]) {
+ assert(v == _u(_v(k), _e(k)));
+ } else {
+ assert(v == _v(k));
+ }
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v)) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < (sizeof(to_update) / sizeof(to_update[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0); CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ return r;
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original); CKERR(chk_r); }
+ });
+ });
+
+ {
+ DB_TXN *txn_2, *txn_3;
+ { int chk_r = env->txn_begin(env, NULL, &txn_2, DB_TXN_SNAPSHOT); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_2, db, chk_original); CKERR(chk_r); }
+ { int chk_r = env->txn_begin(env, NULL, &txn_3, 0); CKERR(chk_r); }
+ { int chk_r = do_updates(txn_3, db); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_2, db, chk_original); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_3, db, chk_updated); CKERR(chk_r); }
+ { int chk_r = txn_2->abort(txn_2); CKERR(chk_r); }
+ { int chk_r = txn_3->abort(txn_3); CKERR(chk_r); }
+ }
+
+ IN_TXN_COMMIT(env, NULL, txn_4, 0, {
+ { int chk_r = do_verify_results(txn_4, db, chk_original); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_txn_snapshot_works_correctly_with_deletes.cc b/storage/tokudb/PerconaFT/src/tests/test_update_txn_snapshot_works_correctly_with_deletes.cc
new file mode 100644
index 00000000000..9cbda2abdcd
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_txn_snapshot_works_correctly_with_deletes.cc
@@ -0,0 +1,168 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that an update doesn't infringe on other txns started with
+// TXN_SNAPSHOT, when the update deletes elements
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+const int to_delete[] = { 0, 1, 1, 1, 0, 0, 1, 0, 1, 0 };
+
+static inline unsigned int _v(const unsigned int i) { return 10 - i; }
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *UU(old_val), const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ assert(extra->size == 0);
+
+ set_val(NULL, set_extra);
+
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_inserts(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, v;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, &v, sizeof(v));
+ for (i = 0; i < (sizeof(to_delete) / sizeof(to_delete[0])); ++i) {
+ v = _v(i);
+ r = db->put(db, txn, keyp, valp, 0); CKERR(r);
+ }
+ return r;
+}
+
+static int do_updates(DB_TXN *txn, DB *db) {
+ int r = 0;
+ DBT key, extra;
+ unsigned int i;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *extrap = dbt_init(&extra, NULL, 0);
+ for (i = 0; i < (sizeof(to_delete) / sizeof(to_delete[0])); ++i) {
+ if (to_delete[i] == 1) {
+ r = db->update(db, txn, keyp, extrap, 0); CKERR(r);
+ }
+ }
+ return r;
+}
+
+static void chk_original(const unsigned int k, const unsigned int v) {
+ assert(v == _v(k));
+}
+
+static int do_verify_results(DB_TXN *txn, DB *db, void (*check_val)(const unsigned int k, const unsigned int v), bool already_deleted) {
+ int r = 0;
+ DBT key, val;
+ unsigned int i, *vp;
+ DBT *keyp = dbt_init(&key, &i, sizeof(i));
+ DBT *valp = dbt_init(&val, NULL, 0);
+ for (i = 0; i < (sizeof(to_delete) / sizeof(to_delete[0])); ++i) {
+ r = db->get(db, txn, keyp, valp, 0);
+ if (already_deleted && to_delete[i]) {
+ CKERR2(r, DB_NOTFOUND);
+ r = 0;
+ } else {
+ CKERR(r);
+ assert(val.size == sizeof(*vp));
+ CAST_FROM_VOIDP(vp, val.data);
+ check_val(i, *vp);
+ }
+ }
+ return r;
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ setup();
+
+ DB *db;
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+
+ { int chk_r = do_inserts(txn_1, db); CKERR(chk_r); }
+
+ IN_TXN_COMMIT(env, txn_1, txn_11, 0, {
+ { int chk_r = do_verify_results(txn_11, db, chk_original, false); CKERR(chk_r); }
+ });
+ });
+
+ {
+ DB_TXN *txn_2, *txn_3;
+ { int chk_r = env->txn_begin(env, NULL, &txn_2, DB_TXN_SNAPSHOT); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_2, db, chk_original, false); CKERR(chk_r); }
+ { int chk_r = env->txn_begin(env, NULL, &txn_3, 0); CKERR(chk_r); }
+ { int chk_r = do_updates(txn_3, db); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_2, db, chk_original, false); CKERR(chk_r); }
+ { int chk_r = do_verify_results(txn_3, db, chk_original, true); CKERR(chk_r); }
+ { int chk_r = txn_2->abort(txn_2); CKERR(chk_r); }
+ { int chk_r = txn_3->abort(txn_3); CKERR(chk_r); }
+ }
+
+ IN_TXN_COMMIT(env, NULL, txn_4, 0, {
+ { int chk_r = do_verify_results(txn_4, db, chk_original, false); CKERR(chk_r); }
+ });
+
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+
+ cleanup();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_update_with_empty_table.cc b/storage/tokudb/PerconaFT/src/tests/test_update_with_empty_table.cc
new file mode 100644
index 00000000000..411c3c4f492
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_update_with_empty_table.cc
@@ -0,0 +1,142 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// test that update broadcast does nothing if the table is empty
+
+#include "test.h"
+
+const int envflags = DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE;
+
+DB_ENV *env;
+
+static int update_fun(DB *UU(db),
+ const DBT *UU(key),
+ const DBT *UU(old_val), const DBT *UU(extra),
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ set_val(extra,set_extra);
+ return 0;
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int chk_r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+ { int chk_r = db_env_create(&env, 0); CKERR(chk_r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, update_fun);
+ { int chk_r = env->open(env, TOKU_TEST_FILENAME, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(chk_r); }
+}
+
+static void cleanup (void) {
+ { int chk_r = env->close(env, 0); CKERR(chk_r); }
+}
+
+static int do_updates(DB_TXN *txn, DB *db, uint32_t flags) {
+ DBT key, val;
+ uint32_t k = 101;
+ uint32_t v = 10101;
+ dbt_init(&key, &k, sizeof(k));
+ dbt_init(&val, &v, sizeof(v));
+
+ int r = db->update(db, txn, &key, &val, flags); CKERR(r);
+ return r;
+}
+
+static void run_test(bool prelock, bool commit) {
+ DB *db;
+ uint32_t update_flags = 0;
+ setup();
+
+ IN_TXN_COMMIT(env, NULL, txn_1, 0, {
+ { int chk_r = db_create(&db, env, 0); CKERR(chk_r); }
+ { int chk_r = db->open(db, txn_1, "foo.db", NULL, DB_BTREE, DB_CREATE, 0666); CKERR(chk_r); }
+ });
+ if (prelock) {
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = db->pre_acquire_table_lock(db, txn_2); CKERR(chk_r); }
+ });
+ }
+
+ if (commit) {
+ IN_TXN_COMMIT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, update_flags); CKERR(chk_r); }
+ });
+ DBC *cursor = NULL;
+ DBT key, val;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db->cursor(db, txn_3, &cursor, 0); CKERR(chk_r); }
+ { int chk_r = cursor->c_get(cursor, &key, &val, DB_NEXT); CKERR(chk_r); }
+ assert(key.size == sizeof(uint32_t));
+ assert(val.size == sizeof(uint32_t));
+ assert(*(uint32_t *)(key.data) == 101);
+ assert(*(uint32_t *)(val.data) == 10101);
+ { int chk_r = cursor->c_close(cursor); CKERR(chk_r); }
+ });
+ }
+ else {
+ IN_TXN_ABORT(env, NULL, txn_2, 0, {
+ { int chk_r = do_updates(txn_2, db, update_flags); CKERR(chk_r); }
+ });
+ DBC *cursor = NULL;
+ DBT key, val;
+ memset(&key, 0, sizeof(key));
+ memset(&val, 0, sizeof(val));
+
+ IN_TXN_COMMIT(env, NULL, txn_3, 0, {
+ { int chk_r = db->cursor(db, txn_3, &cursor, 0); CKERR(chk_r); }
+ { int chk_r = cursor->c_get(cursor, &key, &val, DB_NEXT); CKERR2(chk_r, DB_NOTFOUND); }
+ { int chk_r = cursor->c_close(cursor); CKERR(chk_r); }
+ });
+ }
+ { int chk_r = db->close(db, 0); CKERR(chk_r); }
+ cleanup();
+}
+
+int test_main(int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ run_test(true,true);
+ run_test(false,true);
+ run_test(true,false);
+ run_test(false,false);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_updates_single_key.cc b/storage/tokudb/PerconaFT/src/tests/test_updates_single_key.cc
new file mode 100644
index 00000000000..3c657a9b32a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_updates_single_key.cc
@@ -0,0 +1,101 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <db.h>
+
+
+//
+// This test ensures that we can do many updates to a single key when the dictionary
+// is just that key.
+//
+static void
+run_test (void) {
+
+ DB_ENV * env;
+ DB *db;
+ const char * const fname = "test.updates_single_key.ft_handle";
+ int r;
+
+ r = db_env_create(&env, 0); assert(r == 0);
+ env->set_errfile(env, stderr);
+ // no need to run with logging, so DB_INIT_LOG not passed in
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL | DB_INIT_TXN | DB_INIT_LOCK | DB_THREAD, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0); assert(r == 0);
+ db->set_errfile(db,stderr); // Turn off those annoying errors
+ r = db->open(db, NULL, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ int i;
+ for (i=0; i<1000000; i++) {
+ int k = 1;
+ int v = i;
+ DBT key, val;
+ DB_TXN* txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ // want this test to go as fast as possible, so no need to use the lock tree
+ // we just care to see that #5700 is behaving better, that some garbage collection is happening
+ r = db->put(db, txn, dbt_init(&key, &k, sizeof k), dbt_init(&val, &v, sizeof v), DB_PRELOCKED_WRITE);
+ txn->commit(txn, DB_TXN_NOSYNC);
+ CKERR(r);
+ }
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ run_test();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_weakxaction.cc b/storage/tokudb/PerconaFT/src/tests/test_weakxaction.cc
new file mode 100644
index 00000000000..1ddb18389f3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_weakxaction.cc
@@ -0,0 +1,101 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+/* Find out about weak transactions.
+ * User A does a transaction.
+ * User B does somethign without a transaction, and it conflicts.
+ */
+
+
+#include <db.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <sys/stat.h>
+
+static void
+test_autotxn (uint32_t env_flags, uint32_t db_flags) {
+ DB_ENV *env;
+ DB *db;
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ r = db_env_create (&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_flags(env, env_flags, 1); CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL |
+ DB_INIT_LOG | DB_INIT_TXN | DB_INIT_LOCK, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ {
+ DB_TXN *x = NULL;
+ if (env_flags==0 && db_flags==0) {
+ r = env->txn_begin(env, 0, &x, 0); CKERR(r);
+ }
+ r = db->open(db, x, "numbers.db", 0, DB_BTREE, DB_CREATE | db_flags, 0);
+ if (env_flags==0 && db_flags==0) {
+ r = x->commit(x, 0); CKERR(r);
+ }
+ CKERR(r);
+ }
+
+ DB_TXN *x1, *x2 = NULL;
+ r = env->txn_begin(env, 0, &x1, DB_TXN_NOWAIT); CKERR(r);
+ DBT k1,k2,v1,v2;
+ dbt_init(&k1, "hello", sizeof "hello");
+ dbt_init(&k2, "hello", sizeof "hello");
+ dbt_init(&v1, "there", sizeof "there");
+ dbt_init(&v2, NULL, 0);
+ memset(&v1, 0, sizeof(DBT));
+ memset(&v2, 0, sizeof(DBT));
+ r = db->put(db, x1, &k1, &v1, 0); CKERR(r);
+ r = db->get(db, x2, &k2, &v2, 0); assert(r==DB_LOCK_DEADLOCK || r==DB_LOCK_NOTGRANTED);
+ r = x1->commit(x1, 0); CKERR(r);
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); assert(r==0);
+}
+
+int
+test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+ test_autotxn(DB_AUTO_COMMIT, DB_AUTO_COMMIT);
+ test_autotxn(0, DB_AUTO_COMMIT);
+ test_autotxn(DB_AUTO_COMMIT, 0);
+ test_autotxn(0, 0);
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/test_zero_length_keys.cc b/storage/tokudb/PerconaFT/src/tests/test_zero_length_keys.cc
new file mode 100644
index 00000000000..d98fea8eead
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_zero_length_keys.cc
@@ -0,0 +1,188 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <db.h>
+
+static void
+walk (DB *db) {
+ int r;
+ DB_TXN * const null_txn = 0;
+
+ DBC *cursor;
+ r = db->cursor(db, null_txn, &cursor, 0); assert(r == 0);
+
+ DBT key; memset(&key, 0, sizeof key); key.flags = DB_DBT_REALLOC;
+ DBT val; memset(&val, 0, sizeof val); val.flags = DB_DBT_REALLOC;
+ int i;
+ for (i=0; ; i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ if (verbose) printf("%d %u %u\n", i, key.size, val.size);
+ if (i == 0) assert(key.size == 0);
+ }
+ assert(i != 0);
+ r = cursor->c_close(cursor); assert(r == 0);
+
+ if (key.data) toku_free(key.data);
+ if (val.data) toku_free(val.data);
+}
+
+static void
+test_insert_zero_length (int n, int dup_mode, const char *fname) {
+ if (verbose) printf("test_insert_zero_length:%d %d\n", n, dup_mode);
+
+ DB_TXN * const null_txn = 0;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_flags(db, dup_mode); assert(r == 0);
+ r = db->set_pagesize(db, 4096); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ int i;
+ for (i=0; i<n; i++) {
+ char k[n]; memset(k, i, n);
+ char v[n]; memset(v, i, n);
+ DBT key;
+ DBT val;
+ r = db->put(db, null_txn, dbt_init(&key, &k, i), dbt_init(&val, &v, i), 0);
+ if (r != 0) {
+ if (verbose) printf("db->put %d %d = %d\n", n, n, r);
+ assert(r == 0);
+ }
+ if (i == 0) {
+ dbt_init(&key, &k, i);
+ memset(&val, 0, sizeof val);
+ r = db->get(db, null_txn, &key, &val, 0);
+ assert(r == 0 && val.data == 0 && val.size == 0);
+
+ r = db->get(db, null_txn, &key, dbt_init_malloc(&val), 0);
+ assert(r == 0 && val.data != 0 && val.size == 0);
+ toku_free(val.data);
+
+ memset(&key, 0, sizeof key);
+ memset(&val, 0, sizeof val);
+ r = db->get(db, null_txn, &key, &val, 0);
+ assert(r == 0 && val.data == 0 && val.size == 0);
+
+ r = db->get(db, null_txn, &key, dbt_init_malloc(&val), 0);
+ assert(r == 0 && val.data != 0 && val.size == 0);
+ toku_free(val.data);
+ }
+ }
+
+ walk(db);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+static void
+test_insert_zero_length_keys (int n, int dup_mode, const char *fname) {
+ if (verbose) printf("test_insert_zero_length_keys:%d %d\n", n, dup_mode);
+
+ DB_TXN * const null_txn = 0;
+ int r;
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ /* create the dup database file */
+ DB_ENV *env;
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_CREATE+DB_PRIVATE+DB_INIT_MPOOL, 0); assert(r == 0);
+
+ DB *db;
+ r = db_create(&db, env, 0); assert(r == 0);
+ r = db->set_flags(db, dup_mode); assert(r == 0);
+ r = db->set_pagesize(db, 4096); assert(r == 0);
+ r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE, 0666); assert(r == 0);
+
+ int i;
+ for (i=0; i<n; i++) {
+ char k[n]; memset(k, i, n);
+ char v[n]; memset(v, i, n);
+ DBT key;
+ DBT val;
+ r = db->put(db, null_txn, dbt_init(&key, &k, 0), dbt_init(&val, &v, i), 0);
+ if (r != 0) {
+ if (verbose) printf("db->put %d %d = %d\n", n, n, r);
+ assert(r == 0);
+ }
+ }
+
+ walk(db);
+
+ r = db->close(db, 0); assert(r == 0);
+ r = env->close(env, 0); assert(r == 0);
+}
+
+int
+test_main(int argc, char *const argv[]) {
+
+ parse_args(argc, argv);
+
+#define TFILE __FILE__ ".tktrace"
+ unlink(TFILE);
+ SET_TRACE_FILE(TFILE);
+
+ test_insert_zero_length(32, 0, "test0");
+ test_insert_zero_length_keys(32, 0, "test0keys");
+
+ CLOSE_TRACE_FILE();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/threaded_stress_test_helpers.h b/storage/tokudb/PerconaFT/src/tests/threaded_stress_test_helpers.h
new file mode 100644
index 00000000000..f2bacceed9d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/threaded_stress_test_helpers.h
@@ -0,0 +1,2898 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+// The Way Things Work:
+//
+// Threaded stress tests have the following properties:
+// - One or more DBs
+// - One or more threads performing some number of operations per txn.
+// - Correctness tests use signed 4 byte keys and signed 4 byte values. They expect
+// a table with all zeroes before running.
+// - Performance tests should use 8 byte keys and 8+ byte values, where the values
+// are some mixture of random uncompressible garbage and zeroes, depending how
+// compressible we want the data. These tests want the table to be populated
+// with keys in the range [0, table_size - 1] unless disperse_keys is true,
+// then the keys are scrambled up in the integer key space.
+
+#include "toku_config.h"
+#include "test.h"
+
+#include <stdio.h>
+#include <math.h>
+#include <locale.h>
+
+#include <db.h>
+#include <memory.h>
+#include <toku_race_tools.h>
+
+#include <portability/toku_atomic.h>
+#include <portability/toku_pthread.h>
+#include <portability/toku_random.h>
+#include <portability/toku_time.h>
+
+#include <src/ydb-internal.h>
+
+#include <util/dbt.h>
+
+#include <util/rwlock.h>
+#include <util/kibbutz.h>
+
+static const size_t min_val_size = sizeof(int32_t);
+static const size_t min_key_size = sizeof(int32_t);
+
+volatile bool run_test; // should be volatile since we are communicating through this variable.
+
+typedef struct arg *ARG;
+typedef int (*operation_t)(DB_TXN *txn, ARG arg, void *operation_extra, void *stats_extra);
+
+// TODO: Properly define these in db.h so we don't have to copy them here
+typedef int (*test_update_callback_f)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra);
+typedef int (*test_generate_row_for_put_callback)(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, DBT_ARRAY *dest_vals, const DBT *src_key, const DBT *src_data);
+typedef int (*test_generate_row_for_del_callback)(DB *dest_db, DB *src_db, DBT_ARRAY *dest_keys, const DBT *src_key, const DBT *src_data);
+
+enum stress_lock_type {
+ STRESS_LOCK_NONE = 0,
+ STRESS_LOCK_SHARED,
+ STRESS_LOCK_EXCL
+};
+
+struct env_args {
+ int fanout;
+ int node_size;
+ int basement_node_size;
+ int rollback_node_size;
+ int checkpointing_period;
+ int cleaner_period;
+ int cleaner_iterations;
+ int sync_period;
+ uint64_t lk_max_memory;
+ uint64_t cachetable_size;
+ uint32_t num_bucket_mutexes;
+ const char *envdir;
+ test_update_callback_f update_function; // update callback function
+ test_generate_row_for_put_callback generate_put_callback;
+ test_generate_row_for_del_callback generate_del_callback;
+};
+
+enum perf_output_format {
+ HUMAN = 0,
+ CSV,
+ TSV,
+ NUM_OUTPUT_FORMATS
+};
+
+struct cli_args {
+ int num_elements; // number of elements per DB
+ int num_DBs; // number of DBs
+ int num_seconds; // how long test should run
+ int join_timeout; // how long to wait for threads to join before assuming deadlocks
+ bool only_create; // true if want to only create DBs but not run stress
+ bool only_stress; // true if DBs are already created and want to only run stress
+ int update_broadcast_period_ms; // specific to test_stress3
+ int num_ptquery_threads; // number of threads to run point queries
+ bool do_test_and_crash; // true if we should crash after running stress test. For recovery tests.
+ bool do_recover; // true if we should run recover
+ int num_update_threads; // number of threads running updates
+ int num_put_threads; // number of threads running puts
+ int range_query_limit; // how many rows to look at for range queries
+ bool serial_insert;
+ bool interleave; // for insert benchmarks, whether to interleave separate threads' puts (or segregate them)
+ bool crash_on_operation_failure;
+ bool print_performance;
+ bool print_thread_performance;
+ bool print_iteration_performance;
+ enum perf_output_format perf_output_format;
+ enum toku_compression_method compression_method; // the compression method to use on newly created DBs
+ int performance_period;
+ uint32_t txn_size; // specifies number of updates/puts/whatevers per txn
+ uint32_t key_size; // number of bytes in vals. Must be at least 4
+ uint32_t val_size; // number of bytes in vals. Must be at least 4
+ double compressibility; // the row values should compress down to this fraction
+ struct env_args env_args; // specifies environment variables
+ bool single_txn;
+ bool warm_cache; // warm caches before running stress_table
+ bool blackhole; // all message injects are no-ops. helps measure txn/logging/locktree overhead.
+ bool nolocktree; // use this flag to avoid the locktree on insertions
+ bool unique_checks; // use uniqueness checking during insert. makes it slow.
+ uint32_t sync_period; // background log fsync period
+ bool nolog; // do not log. useful for testing in memory performance.
+ bool nocrashstatus; // do not print engine status upon crash
+ bool prelock_updates; // update threads perform serial updates on a prelocked range
+ bool disperse_keys; // spread the keys out during a load (by reversing the bits in the loop index) to make a wide tree we can spread out random inserts into
+ bool memcmp_keys; // pack keys big endian and use the builtin key comparison function in the fractal tree
+ bool direct_io; // use direct I/O
+ const char *print_engine_status; // print engine status rows matching a simple regex "a|b|c", matching strings where a or b or c is a subtring.
+};
+
+struct arg {
+ DB **dbp; // array of DBs
+ DB_ENV* env; // environment used
+ bool bounded_element_range; // true if elements in dictionary are bounded
+ // by num_elements, that is, all keys in each
+ // DB are in [0, num_elements)
+ // false otherwise
+ int sleep_ms; // number of milliseconds to sleep between operations
+ uint32_t txn_flags; // isolation level for txn running operation
+ operation_t operation; // function that is the operation to be run
+ void* operation_extra; // extra parameter passed to operation
+ enum stress_lock_type lock_type; // states if operation must be exclusive, shared, or does not require locking
+ struct random_data *random_data; // state for random_r
+ int thread_idx;
+ int num_threads;
+ struct cli_args *cli;
+ bool do_prepare;
+ bool prelock_updates;
+ bool track_thread_performance;
+ bool wrap_in_parent;
+};
+
+static void arg_init(struct arg *arg, DB **dbp, DB_ENV *env, struct cli_args *cli_args) {
+ arg->cli = cli_args;
+ arg->dbp = dbp;
+ arg->env = env;
+ arg->bounded_element_range = true;
+ arg->sleep_ms = 0;
+ arg->lock_type = STRESS_LOCK_NONE;
+ arg->txn_flags = DB_TXN_SNAPSHOT;
+ arg->operation_extra = nullptr;
+ arg->do_prepare = false;
+ arg->prelock_updates = false;
+ arg->track_thread_performance = true;
+ arg->wrap_in_parent = false;
+}
+
+enum operation_type {
+ OPERATION = 0,
+ PUTS,
+ PTQUERIES,
+ NUM_OPERATION_TYPES
+};
+
+const char *operation_names[] = {
+ "ops",
+ "puts",
+ "ptqueries",
+ nullptr
+};
+
+static void increment_counter(void *extra, enum operation_type type, uint64_t inc) {
+ invariant(type != OPERATION);
+ int t = (int) type;
+ invariant(extra);
+ invariant(t >= 0 && t < (int) NUM_OPERATION_TYPES);
+ uint64_t *CAST_FROM_VOIDP(counters, extra);
+ counters[t] += inc;
+}
+
+struct perf_formatter {
+ void (*header)(const struct cli_args *cli_args, const int num_threads);
+ void (*iteration)(const struct cli_args *cli_args, const int current_time, uint64_t last_counters[][(int) NUM_OPERATION_TYPES], uint64_t *counters[], const int num_threads);
+ void (*totals)(const struct cli_args *cli_args, uint64_t *counters[], const int num_threads);
+};
+
+static inline int
+seconds_in_this_iteration(const int current_time, const int performance_period)
+{
+ const int iteration = (current_time + performance_period - 1) / performance_period;
+ return current_time - ((iteration - 1) * performance_period);
+}
+
+static void
+human_print_perf_header(const struct cli_args *UU(cli_args), const int UU(num_threads)) {}
+
+static void
+human_print_perf_iteration(const struct cli_args *cli_args, const int current_time, uint64_t last_counters[][(int) NUM_OPERATION_TYPES], uint64_t *counters[], const int num_threads)
+{
+ const int secondsthisiter = seconds_in_this_iteration(current_time, cli_args->performance_period);
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ uint64_t period_total = 0;
+ printf("%4d %s", current_time, operation_names[op]);
+ for (int i = strlen(operation_names[op]); i < 12; ++i) {
+ printf(" ");
+ }
+ for (int t = 0; t < num_threads; ++t) {
+ const uint64_t last = last_counters[t][op];
+ const uint64_t current = counters[t][op];
+ const uint64_t this_iter = current - last;
+ if (cli_args->print_thread_performance) {
+ const double persecond = (double) this_iter / secondsthisiter;
+ printf("\t%'12" PRIu64 " (%'12.1lf/s)", this_iter, persecond);
+ }
+ period_total += this_iter;
+ last_counters[t][op] = current;
+ }
+ const double totalpersecond = (double) period_total / secondsthisiter;
+ printf("\tTotal %'12" PRIu64 " (%'12.1lf/s)\n", period_total, totalpersecond);
+ }
+ fflush(stdout);
+}
+
+static void
+human_print_perf_totals(const struct cli_args *cli_args, uint64_t *counters[], const int num_threads)
+{
+ if (cli_args->print_iteration_performance) {
+ printf("\n");
+ }
+ printf("Overall performance:\n");
+ uint64_t overall_totals[(int) NUM_OPERATION_TYPES];
+ ZERO_ARRAY(overall_totals);
+ for (int t = 0; t < num_threads; ++t) {
+ if (cli_args->print_thread_performance) {
+ printf("Thread %4d: ", t + 1);
+ }
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const uint64_t current = counters[t][op];
+ if (cli_args->print_thread_performance) {
+ const double persecond = (double) current / cli_args->num_seconds;
+ printf("\t%s\t%'12" PRIu64 " (%'12.1lf/s)", operation_names[op], current, persecond);
+ }
+ overall_totals[op] += current;
+ }
+ if (cli_args->print_thread_performance) {
+ printf("\n");
+ }
+ }
+ printf("All threads: ");
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const double totalpersecond = (double) overall_totals[op] / cli_args->num_seconds;
+ printf("\t%s\t%'12" PRIu64 " (%'12.1lf/s)", operation_names[op], overall_totals[op], totalpersecond);
+ }
+ printf("\n");
+}
+
+static void
+csv_print_perf_header(const struct cli_args *cli_args, const int num_threads)
+{
+ printf("seconds");
+ if (cli_args->print_thread_performance) {
+ for (int t = 1; t <= num_threads; ++t) {
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ printf(",\"Thread %d %s\",\"Thread %d %s/s\"", t, operation_names[op], t, operation_names[op]);
+ }
+ }
+ }
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ printf(",\"Total %s\",\"Total %s/s\"", operation_names[op], operation_names[op]);
+ }
+ printf("\n");
+}
+
+static void
+csv_print_perf_iteration(const struct cli_args *cli_args, const int current_time, uint64_t last_counters[][(int) NUM_OPERATION_TYPES], uint64_t *counters[], const int num_threads)
+{
+ const int secondsthisiter = seconds_in_this_iteration(current_time, cli_args->performance_period);
+ printf("%d", current_time);
+ uint64_t period_totals[(int) NUM_OPERATION_TYPES];
+ ZERO_ARRAY(period_totals);
+ for (int t = 0; t < num_threads; ++t) {
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const uint64_t last = last_counters[t][op];
+ const uint64_t current = counters[t][op];
+ const uint64_t this_iter = current - last;
+ if (cli_args->print_thread_performance) {
+ const double persecond = (double) this_iter / secondsthisiter;
+ printf(",%" PRIu64 ",%.1lf", this_iter, persecond);
+ }
+ period_totals[op] += this_iter;
+ last_counters[t][op] = current;
+ }
+ }
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const double totalpersecond = (double) period_totals[op] / secondsthisiter;
+ printf(",%" PRIu64 ",%.1lf", period_totals[op], totalpersecond);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+static void
+csv_print_perf_totals(const struct cli_args *cli_args, uint64_t *counters[], const int num_threads) {
+ printf("overall");
+ uint64_t overall_totals[(int) NUM_OPERATION_TYPES];
+ ZERO_ARRAY(overall_totals);
+ for (int t = 0; t < num_threads; ++t) {
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const uint64_t current = counters[t][op];
+ if (cli_args->print_thread_performance) {
+ const double persecond = (double) current / cli_args->num_seconds;
+ printf(",%" PRIu64 ",%.1lf", current, persecond);
+ }
+ overall_totals[op] += current;
+ }
+ }
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const double totalpersecond = (double) overall_totals[op] / cli_args->num_seconds;
+ printf(",%" PRIu64 ",%.1lf", overall_totals[op], totalpersecond);
+ }
+ printf("\n");
+}
+
+static void
+tsv_print_perf_header(const struct cli_args *cli_args, const int num_threads)
+{
+ printf("\"seconds\"");
+ if (cli_args->print_thread_performance) {
+ for (int t = 1; t <= num_threads; ++t) {
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ printf("\t\"Thread %d %s\"\t\"Thread %d %s/s\"", t, operation_names[op], t, operation_names[op]);
+ }
+ }
+ }
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ printf("\t\"Total %s\"\t\"Total %s/s\"", operation_names[op], operation_names[op]);
+ }
+ printf("\n");
+}
+
+static void
+tsv_print_perf_iteration(const struct cli_args *cli_args, const int current_time, uint64_t last_counters[][(int) NUM_OPERATION_TYPES], uint64_t *counters[], const int num_threads)
+{
+ const int secondsthisiter = seconds_in_this_iteration(current_time, cli_args->performance_period);
+ printf("%d", current_time);
+ uint64_t period_totals[(int) NUM_OPERATION_TYPES];
+ ZERO_ARRAY(period_totals);
+ for (int t = 0; t < num_threads; ++t) {
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const uint64_t last = last_counters[t][op];
+ const uint64_t current = counters[t][op];
+ const uint64_t this_iter = current - last;
+ if (cli_args->print_thread_performance) {
+ const double persecond = (double) this_iter / secondsthisiter;
+ printf("\t%" PRIu64 "\t%.1lf", this_iter, persecond);
+ }
+ period_totals[op] += this_iter;
+ last_counters[t][op] = current;
+ }
+ }
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const double totalpersecond = (double) period_totals[op] / secondsthisiter;
+ printf("\t%" PRIu64 "\t%.1lf", period_totals[op], totalpersecond);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+static void
+tsv_print_perf_totals(const struct cli_args *cli_args, uint64_t *counters[], const int num_threads) {
+ printf("\"overall\"");
+ uint64_t overall_totals[(int) NUM_OPERATION_TYPES];
+ ZERO_ARRAY(overall_totals);
+ for (int t = 0; t < num_threads; ++t) {
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const uint64_t current = counters[t][op];
+ if (cli_args->print_thread_performance) {
+ const double persecond = (double) current / cli_args->num_seconds;
+ printf("\t%" PRIu64 "\t%.1lf", current, persecond);
+ }
+ overall_totals[op] += current;
+ }
+ }
+ for (int op = 0; op < (int) NUM_OPERATION_TYPES; ++op) {
+ const double totalpersecond = (double) overall_totals[op] / cli_args->num_seconds;
+ printf("\t%" PRIu64 "\t%.1lf", overall_totals[op], totalpersecond);
+ }
+ printf("\n");
+}
+
+const struct perf_formatter perf_formatters[] = {
+ [HUMAN] = {
+ .header = human_print_perf_header,
+ .iteration = human_print_perf_iteration,
+ .totals = human_print_perf_totals
+ },
+ [CSV] = {
+ .header = csv_print_perf_header,
+ .iteration = csv_print_perf_iteration,
+ .totals = csv_print_perf_totals
+ },
+ [TSV] = {
+ .header = tsv_print_perf_header,
+ .iteration = tsv_print_perf_iteration,
+ .totals = tsv_print_perf_totals
+ },
+};
+
+static int get_env_open_flags(struct cli_args *args) {
+ int flags = DB_INIT_LOCK|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE;
+ flags |= args->nolog ? 0 : DB_INIT_LOG;
+ return flags;
+}
+
+static int get_put_flags(struct cli_args *args) {
+ int flags = 0;
+ flags |= args->nolocktree ? DB_PRELOCKED_WRITE : 0;
+ flags |= args->unique_checks ? DB_NOOVERWRITE : 0;
+ return flags;
+}
+
+static int get_commit_flags(struct cli_args *args) {
+ int flags = 0;
+ flags |= args->env_args.sync_period > 0 ? DB_TXN_NOSYNC : 0;
+ return flags;
+}
+
+struct worker_extra {
+ struct arg* thread_arg;
+ toku_mutex_t *operation_lock_mutex;
+ struct rwlock *operation_lock;
+ uint64_t *counters;
+ int64_t pad[4]; // pad to 64 bytes
+};
+
+static void lock_worker_op(struct worker_extra* we) {
+ ARG arg = we->thread_arg;
+ if (arg->lock_type != STRESS_LOCK_NONE) {
+ toku_mutex_lock(we->operation_lock_mutex);
+ if (arg->lock_type == STRESS_LOCK_SHARED) {
+ rwlock_read_lock(we->operation_lock, we->operation_lock_mutex);
+ } else if (arg->lock_type == STRESS_LOCK_EXCL) {
+ rwlock_write_lock(we->operation_lock, we->operation_lock_mutex);
+ } else {
+ abort();
+ }
+ toku_mutex_unlock(we->operation_lock_mutex);
+ }
+}
+
+static void unlock_worker_op(struct worker_extra* we) {
+ ARG arg = we->thread_arg;
+ if (arg->lock_type != STRESS_LOCK_NONE) {
+ toku_mutex_lock(we->operation_lock_mutex);
+ if (arg->lock_type == STRESS_LOCK_SHARED) {
+ rwlock_read_unlock(we->operation_lock);
+ } else if (arg->lock_type == STRESS_LOCK_EXCL) {
+ rwlock_write_unlock(we->operation_lock);
+ } else {
+ abort();
+ }
+ toku_mutex_unlock(we->operation_lock_mutex);
+ }
+}
+
+static void *worker(void *arg_v) {
+ int r;
+ struct worker_extra* CAST_FROM_VOIDP(we, arg_v);
+ ARG arg = we->thread_arg;
+ struct random_data random_data;
+ ZERO_STRUCT(random_data);
+ char *XCALLOC_N(8, random_buf);
+ r = myinitstate_r(random(), random_buf, 8, &random_data);
+ assert_zero(r);
+ arg->random_data = &random_data;
+ DB_ENV *env = arg->env;
+ DB_TXN *txn = nullptr;
+ DB_TXN *ptxn = nullptr;
+ if (verbose) {
+ toku_pthread_t self = toku_pthread_self();
+ uintptr_t intself = (uintptr_t) self;
+ printf("%lu starting %p\n", (unsigned long) intself, arg->operation);
+ }
+ if (arg->cli->single_txn) {
+ r = env->txn_begin(env, 0, &txn, arg->txn_flags); CKERR(r);
+ } else if (arg->wrap_in_parent) {
+ r = env->txn_begin(env, 0, &ptxn, arg->txn_flags); CKERR(r);
+ }
+ while (run_test) {
+ lock_worker_op(we);
+ if (!arg->cli->single_txn) {
+ r = env->txn_begin(env, ptxn, &txn, arg->txn_flags); CKERR(r);
+ }
+ r = arg->operation(txn, arg, arg->operation_extra, we->counters);
+ if (r==0 && !arg->cli->single_txn && arg->do_prepare) {
+ uint8_t gid[DB_GID_SIZE];
+ memset(gid, 0, DB_GID_SIZE);
+ uint64_t gid_val = txn->id64(txn);
+ uint64_t *gid_count_p = cast_to_typeof(gid_count_p) gid; // make gcc --happy about -Wstrict-aliasing
+ *gid_count_p = gid_val;
+ int rr = txn->prepare(txn, gid, 0);
+ assert_zero(rr);
+ }
+ if (r == 0) {
+ if (!arg->cli->single_txn) {
+ int flags = get_commit_flags(arg->cli);
+ int chk_r = txn->commit(txn, flags); CKERR(chk_r);
+ }
+ } else {
+ if (arg->cli->crash_on_operation_failure) {
+ CKERR(r);
+ } else {
+ if (!arg->cli->single_txn) {
+ { int chk_r = txn->abort(txn); CKERR(chk_r); }
+ }
+ }
+ }
+ unlock_worker_op(we);
+ if (arg->track_thread_performance) {
+ we->counters[OPERATION]++;
+ }
+ if (arg->sleep_ms) {
+ usleep(arg->sleep_ms * 1000);
+ }
+ }
+ if (arg->cli->single_txn) {
+ int flags = get_commit_flags(arg->cli);
+ int chk_r = txn->commit(txn, flags); CKERR(chk_r);
+ } else if (arg->wrap_in_parent) {
+ int flags = get_commit_flags(arg->cli);
+ int chk_r = ptxn->commit(ptxn, flags); CKERR(chk_r);
+ }
+ if (verbose) {
+ toku_pthread_t self = toku_pthread_self();
+ uintptr_t intself = (uintptr_t) self;
+ printf("%lu returning\n", (unsigned long) intself);
+ }
+ toku_free(random_buf);
+ return arg;
+}
+
+struct scan_cb_extra {
+ bool fast;
+ int curr_sum;
+ int num_elements;
+};
+
+struct scan_op_extra {
+ bool fast;
+ bool fwd;
+ bool prefetch;
+};
+
+static int
+scan_cb(const DBT *key, const DBT *val, void *arg_v) {
+ struct scan_cb_extra *CAST_FROM_VOIDP(cb_extra, arg_v);
+ assert(key);
+ assert(val);
+ assert(cb_extra);
+ assert(val->size >= sizeof(int));
+ cb_extra->curr_sum += *(int *) val->data;
+ cb_extra->num_elements++;
+ return cb_extra->fast ? TOKUDB_CURSOR_CONTINUE : 0;
+}
+
+static int scan_op_and_maybe_check_sum(
+ DB* db,
+ DB_TXN *txn,
+ struct scan_op_extra* sce,
+ bool check_sum
+ )
+{
+ int r = 0;
+ DBC* cursor = nullptr;
+
+ struct scan_cb_extra e = {
+ e.fast = sce->fast,
+ e.curr_sum = 0,
+ e.num_elements = 0,
+ };
+
+ { int chk_r = db->cursor(db, txn, &cursor, 0); CKERR(chk_r); }
+ if (sce->prefetch) {
+ r = cursor->c_set_bounds(cursor, db->dbt_neg_infty(), db->dbt_pos_infty(), true, 0);
+ assert(r == 0);
+ }
+ while (r != DB_NOTFOUND) {
+ if (sce->fwd) {
+ r = cursor->c_getf_next(cursor, 0, scan_cb, &e);
+ }
+ else {
+ r = cursor->c_getf_prev(cursor, 0, scan_cb, &e);
+ }
+ assert(r==0 || r==DB_NOTFOUND);
+ if (!run_test) {
+ // terminate early because this op takes a while under drd.
+ // don't check the sum if we do this.
+ check_sum = false;
+ break;
+ }
+ }
+ { int chk_r = cursor->c_close(cursor); CKERR(chk_r); }
+ if (r == DB_NOTFOUND) {
+ r = 0;
+ }
+ if (check_sum && e.curr_sum) {
+ printf("e.curr_sum: %" PRId32 " e.num_elements: %" PRId32 " \n", e.curr_sum, e.num_elements);
+ abort();
+ }
+ return r;
+}
+
+static int generate_row_for_put(
+ DB *dest_db,
+ DB *src_db,
+ DBT_ARRAY *dest_keys,
+ DBT_ARRAY *dest_vals,
+ const DBT *src_key,
+ const DBT *src_val
+ )
+{
+ invariant(!src_db || src_db != dest_db);
+ invariant(src_key->size >= sizeof(unsigned int));
+
+ // Consistent pseudo random source. Use checksum of key and val, and which db as seed
+
+/*
+ struct x1764 l;
+ x1764_init(&l);
+ x1764_add(&l, src_key->data, src_key->size);
+ x1764_add(&l, src_val->data, src_val->size);
+ x1764_add(&l, &dest_db, sizeof(dest_db)); //make it depend on which db
+ unsigned int seed = x1764_finish(&l);
+ */
+ unsigned int seed = *(unsigned int*)src_key->data;
+
+ struct random_data random_data;
+ ZERO_STRUCT(random_data);
+ char random_buf[8];
+ {
+ int r = myinitstate_r(seed, random_buf, 8, &random_data);
+ assert_zero(r);
+ }
+
+ uint8_t num_outputs = 0;
+ while (myrandom_r(&random_data) % 2) {
+ num_outputs++;
+ if (num_outputs > 8) {
+ break;
+ }
+ }
+
+ toku_dbt_array_resize(dest_keys, num_outputs);
+ toku_dbt_array_resize(dest_vals, num_outputs);
+ int sum = 0;
+ for (uint8_t i = 0; i < num_outputs; i++) {
+ DBT *dest_key = &dest_keys->dbts[i];
+ DBT *dest_val = &dest_vals->dbts[i];
+
+ invariant(dest_key->flags == DB_DBT_REALLOC);
+ invariant(dest_val->flags == DB_DBT_REALLOC);
+
+ if (dest_key->ulen < src_key->size) {
+ dest_key->data = toku_xrealloc(dest_key->data, src_key->size);
+ dest_key->ulen = src_key->size;
+ }
+ dest_key->size = src_key->size;
+ if (dest_val->ulen < src_val->size) {
+ dest_val->data = toku_xrealloc(dest_val->data, src_val->size);
+ dest_val->ulen = src_val->size;
+ }
+ dest_val->size = src_val->size;
+ memcpy(dest_key->data, src_key->data, src_key->size);
+ ((uint8_t*)dest_key->data)[src_key->size-1] = i; //Have different keys for each entry.
+
+ memcpy(dest_val->data, src_val->data, src_val->size);
+ invariant(dest_val->size >= sizeof(int));
+ int number;
+ if (i == num_outputs - 1) {
+ // Make sum add to 0
+ number = -sum;
+ } else {
+ // Keep track of sum
+ number = myrandom_r(&random_data);
+ }
+ sum += number;
+ *(int *) dest_val->data = number;
+ }
+ invariant(sum == 0);
+ return 0;
+}
+
+// How Keys Work:
+//
+// Keys are either
+// - 4 byte little endian non-negative integers
+// - 8 byte little endian non-negative integers
+// - 8 byte little endian non-negative integers, padded with zeroes.
+//
+// The comparison function treats the key as a 4 byte
+// int if the key size is exactly 4, and it treats
+// the key as an 8 byte int if the key size is 8 or more.
+
+static int64_t random_bounded_key(struct random_data *random_data, ARG arg) {
+// Effect: Returns a random key in the table, possible bounded by the number of elements.
+ int64_t key = myrandom_r(random_data);
+ if (arg->bounded_element_range && arg->cli->num_elements > 0) {
+ key = key % arg->cli->num_elements;
+ }
+ return key;
+}
+
+static int64_t breverse(int64_t v)
+// Effect: return the bits in i, reversed
+// Notes: implementation taken from http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious
+// Rationale: just a hack to spread out the keys during loading, doesn't need to be fast but does need to be correct.
+{
+ uint64_t k = v; // r will be reversed bits of v; first get LSB of v
+ int s = sizeof(v) * CHAR_BIT - 1; // extra shift needed at end
+
+ for (v >>= 1; v; v >>= 1) {
+ k <<= 1;
+ k |= v & 1;
+ s--;
+ }
+ k <<= s; // shift when v's highest bits are zero
+ int64_t r = k;
+ return r & ~(1ULL << 63);
+}
+
+static void
+fill_key_buf(int64_t key, uint8_t *data, struct cli_args *args) {
+// Effect: Fill data with a specific little-endian integer, 4 or 8 bytes long
+// depending on args->key_size, possibly padded with zeroes.
+// Requires: *data is at least sizeof(uint64_t)
+ if (args->disperse_keys) {
+ key = breverse(key);
+ }
+ invariant(key >= 0);
+ if (args->key_size == sizeof(int)) {
+ const int key32 = args->memcmp_keys ? toku_htonl(key) : key;
+ memcpy(data, &key32, sizeof(key32));
+ } else {
+ invariant(args->key_size >= sizeof(key));
+ const int64_t key64 = args->memcmp_keys ? toku_htonl(key) : key;
+ memcpy(data, &key64, sizeof(key64));
+ memset(data + sizeof(key64), 0, args->key_size - sizeof(key64));
+ }
+}
+
+static void
+fill_key_buf_random(struct random_data *random_data, uint8_t *data, ARG arg) {
+// Effect: Fill data with a random, little-endian, 4 or 8 byte integer, possibly
+// bounded by the size of the table, and padded with zeroes until key_size.
+// Requires, Notes: see fill_key_buf()
+ int64_t key = random_bounded_key(random_data, arg);
+ fill_key_buf(key, data, arg->cli);
+}
+
+// How Vals Work:
+//
+// Values are either
+// - 4 byte little endian integers
+// - 4 byte little endian integers, padded with zeroes
+// - X bytes random values, Y bytes zeroes, where X and Y
+// are derived from the desired compressibility;
+//
+// Correctness tests use integer values, perf tests use random bytes.
+// Both support padding out values > 4 bytes with zeroes.
+
+static void
+fill_val_buf(int64_t val, uint8_t *data, uint32_t val_size) {
+// Effect, Requires, Notes: see fill_key_buf().
+ if (val_size == sizeof(int)) {
+ const int val32 = val;
+ memcpy(data, &val32, sizeof(val32));
+ } else {
+ invariant(val_size >= sizeof(val));
+ memcpy(data, &val, sizeof(val));
+ memset(data + sizeof(val), 0, val_size - sizeof(val));
+ }
+}
+
+// Fill array with compressibility*size 0s.
+// 0.0<=compressibility<=1.0
+// Compressibility is the fraction of size that will be 0s (e.g. approximate fraction that will be compressed away).
+// The rest will be random data.
+static void
+fill_val_buf_random(struct random_data *random_data, uint8_t *data, struct cli_args *args) {
+ invariant(args->val_size >= min_val_size);
+ //Requires: The array was zeroed since the last time 'size' was changed.
+ //Requires: compressibility is in range [0,1] indicating fraction that should be zeros.
+
+ // Fill in the random bytes
+ uint32_t num_random_bytes = (1 - args->compressibility) * args->val_size;
+ if (num_random_bytes > 0) {
+ uint32_t filled;
+ for (filled = 0; filled + sizeof(uint64_t) <= num_random_bytes; filled += sizeof(uint64_t)) {
+ *((uint64_t *) &data[filled]) = myrandom_r(random_data);
+ }
+ if (filled != num_random_bytes) {
+ uint64_t last8 = myrandom_r(random_data);
+ memcpy(&data[filled], &last8, num_random_bytes - filled);
+ }
+ }
+
+ // Fill in the zero bytes
+ if (num_random_bytes < args->val_size) {
+ memset(data + num_random_bytes, 0, args->val_size - num_random_bytes);
+ }
+}
+
+static int random_put_in_db(DB *db, DB_TXN *txn, ARG arg, bool ignore_errors, void *stats_extra) {
+ int r = 0;
+ uint8_t keybuf[arg->cli->key_size];
+ uint8_t valbuf[arg->cli->val_size];
+
+ DBT key, val;
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, valbuf, sizeof valbuf);
+ const int put_flags = get_put_flags(arg->cli);
+
+ uint64_t puts_to_increment = 0;
+ for (uint32_t i = 0; i < arg->cli->txn_size; ++i) {
+ fill_key_buf_random(arg->random_data, keybuf, arg);
+ fill_val_buf_random(arg->random_data, valbuf, arg->cli);
+ r = db->put(db, txn, &key, &val, put_flags);
+ if (!ignore_errors && r != 0) {
+ goto cleanup;
+ }
+ puts_to_increment++;
+ if (puts_to_increment == 100) {
+ increment_counter(stats_extra, PUTS, puts_to_increment);
+ puts_to_increment = 0;
+ }
+ }
+
+cleanup:
+ increment_counter(stats_extra, PUTS, puts_to_increment);
+ return r;
+}
+
+static int UU() random_put_op(DB_TXN *txn, ARG arg, void *UU(operation_extra), void *stats_extra) {
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+ return random_put_in_db(db, txn, arg, false, stats_extra);
+}
+
+static int UU() random_put_op_singledb(DB_TXN *txn, ARG arg, void *UU(operation_extra), void *stats_extra) {
+ int db_index = arg->thread_idx%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+ return random_put_in_db(db, txn, arg, false, stats_extra);
+}
+
+struct serial_put_extra {
+ uint64_t current;
+};
+
+static int UU() serial_put_op(DB_TXN *txn, ARG arg, void *operation_extra, void *stats_extra) {
+ struct serial_put_extra *CAST_FROM_VOIDP(extra, operation_extra);
+
+ int db_index = arg->thread_idx % arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+
+ int r = 0;
+ uint8_t keybuf[arg->cli->key_size];
+ uint8_t valbuf[arg->cli->val_size];
+
+ DBT key, val;
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, valbuf, sizeof valbuf);
+ const int put_flags = get_put_flags(arg->cli);
+
+ uint64_t puts_to_increment = 0;
+ for (uint64_t i = 0; i < arg->cli->txn_size; ++i) {
+ // TODO: Change perf_insert to pass a single serial_put_op_extra
+ // to each insertion thread so they share the current key,
+ // and use a sync fetch an add here. This way you can measure
+ // the true performance of multiple threads appending unique
+ // keys to the end of a tree.
+ uint64_t k = extra->current++;
+ fill_key_buf(k, keybuf, arg->cli);
+ fill_val_buf_random(arg->random_data, valbuf, arg->cli);
+ r = db->put(db, txn, &key, &val, put_flags);
+ if (r != 0) {
+ goto cleanup;
+ }
+ puts_to_increment++;
+ if (puts_to_increment == 100) {
+ increment_counter(stats_extra, PUTS, puts_to_increment);
+ puts_to_increment = 0;
+ }
+ }
+
+cleanup:
+ increment_counter(stats_extra, PUTS, puts_to_increment);
+ return r;
+}
+
+struct loader_op_extra {
+ struct scan_op_extra soe;
+ int num_dbs;
+};
+
+static int UU() loader_op(DB_TXN* txn, ARG arg, void* operation_extra, void *UU(stats_extra)) {
+ struct loader_op_extra* CAST_FROM_VOIDP(extra, operation_extra);
+ invariant(extra->num_dbs >= 1);
+ DB_ENV* env = arg->env;
+ int r;
+ for (int num = 0; num < 2; num++) {
+ DB *dbs_load[extra->num_dbs];
+ uint32_t db_flags[extra->num_dbs];
+ uint32_t dbt_flags[extra->num_dbs];
+ for (int i = 0; i < extra->num_dbs; ++i) {
+ db_flags[i] = 0;
+ dbt_flags[i] = 0;
+ r = db_create(&dbs_load[i], env, 0);
+ assert(r == 0);
+ char fname[100];
+ sprintf(fname, "loader-db-%d", i);
+ // TODO: Need to call before_db_open_hook() and after_db_open_hook()
+ r = dbs_load[i]->open(dbs_load[i], txn, fname, nullptr, DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+ }
+ DB_LOADER *loader;
+ uint32_t loader_flags = (num == 0) ? 0 : LOADER_COMPRESS_INTERMEDIATES;
+ r = env->create_loader(env, txn, &loader, dbs_load[0], extra->num_dbs, dbs_load, db_flags, dbt_flags, loader_flags);
+ CKERR(r);
+
+ DBT key, val;
+ uint8_t keybuf[arg->cli->key_size];
+ uint8_t valbuf[arg->cli->val_size];
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, valbuf, sizeof valbuf);
+
+ int sum = 0;
+ const int num_elements = 1000;
+ for (int i = 0; i < num_elements; i++) {
+ fill_key_buf(i, keybuf, arg->cli);
+ fill_val_buf_random(arg->random_data, valbuf, arg->cli);
+
+ assert(val.size >= sizeof(int));
+ if (i == num_elements - 1) {
+ // Make sum add to 0
+ *(int *) val.data = -sum;
+ } else {
+ // Keep track of sum
+ sum += *(int *) val.data;
+ }
+ r = loader->put(loader, &key, &val); CKERR(r);
+ }
+
+ r = loader->close(loader); CKERR(r);
+
+ for (int i = 0; i < extra->num_dbs; ++i) {
+ r = scan_op_and_maybe_check_sum(dbs_load[i], txn, &extra->soe, true); CKERR(r);
+ r = dbs_load[i]->close(dbs_load[i], 0); CKERR(r);
+ char fname[100];
+ sprintf(fname, "loader-db-%d", i);
+ r = env->dbremove(env, txn, fname, nullptr, 0); CKERR(r);
+ }
+ }
+ return 0;
+}
+
+static int UU() keyrange_op(DB_TXN *txn, ARG arg, void* UU(operation_extra), void *UU(stats_extra)) {
+ // Pick a random DB, do a keyrange operation.
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+
+ int r = 0;
+ uint8_t keybuf[arg->cli->key_size];
+
+ DBT key;
+ dbt_init(&key, keybuf, sizeof keybuf);
+ fill_key_buf_random(arg->random_data, keybuf, arg);
+
+ uint64_t less,equal,greater;
+ int is_exact;
+ r = db->key_range64(db, txn, &key, &less, &equal, &greater, &is_exact);
+ assert(r == 0);
+ return r;
+}
+
+static int UU() frag_op(DB_TXN *UU(txn), ARG arg, void* UU(operation_extra), void *UU(stats_extra)) {
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB *db = arg->dbp[db_index];
+
+ TOKU_DB_FRAGMENTATION_S frag;
+ int r = db->get_fragmentation(db, &frag);
+ invariant_zero(r);
+ return r;
+}
+
+static void UU() get_key_after_bytes_callback(const DBT *UU(end_key), uint64_t UU(skipped), void *UU(extra)) {
+ // nothing
+}
+
+static int UU() get_key_after_bytes_op(DB_TXN *txn, ARG arg, void* UU(operation_extra), void *UU(stats_extra)) {
+ // Pick a random DB, do a get_key_after_bytes operation.
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+
+ int r = 0;
+ uint8_t keybuf[arg->cli->key_size];
+
+ DBT start_key, end_key;
+ dbt_init(&start_key, keybuf, sizeof keybuf);
+ fill_key_buf_random(arg->random_data, keybuf, arg);
+ uint64_t skip_len = myrandom_r(arg->random_data) % (2<<30);
+ dbt_init(&end_key, nullptr, 0);
+
+ r = db->get_key_after_bytes(db, txn, &start_key, skip_len, get_key_after_bytes_callback, nullptr, 0);
+ return r;
+}
+
+static int verify_progress_callback(void *UU(extra), float UU(progress)) {
+ if (!run_test) {
+ return -1;
+ }
+ return 0;
+}
+
+static int UU() verify_op(DB_TXN* UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ int r = 0;
+ for (int i = 0; i < arg->cli->num_DBs && run_test; i++) {
+ DB* db = arg->dbp[i];
+ r = db->verify_with_progress(db, verify_progress_callback, nullptr, 1, 0);
+ if (!run_test) {
+ r = 0;
+ }
+ CKERR(r);
+ }
+ return r;
+}
+
+static int UU() scan_op(DB_TXN *txn, ARG arg, void* operation_extra, void *UU(stats_extra)) {
+ struct scan_op_extra* CAST_FROM_VOIDP(extra, operation_extra);
+ for (int i = 0; run_test && i < arg->cli->num_DBs; i++) {
+ int r = scan_op_and_maybe_check_sum(arg->dbp[i], txn, extra, true);
+ assert_zero(r);
+ }
+ return 0;
+}
+
+static int UU() scan_op_no_check(DB_TXN *txn, ARG arg, void* operation_extra, void *UU(stats_extra)) {
+ struct scan_op_extra* CAST_FROM_VOIDP(extra, operation_extra);
+ for (int i = 0; run_test && i < arg->cli->num_DBs; i++) {
+ int r = scan_op_and_maybe_check_sum(arg->dbp[i], txn, extra, false);
+ assert_zero(r);
+ }
+ return 0;
+}
+
+struct scan_op_worker_info {
+ DB *db;
+ DB_TXN *txn;
+ void *extra;
+};
+
+static void scan_op_worker(void *arg) {
+ struct scan_op_worker_info *CAST_FROM_VOIDP(info, arg);
+ struct scan_op_extra *CAST_FROM_VOIDP(extra, info->extra);
+ int r = scan_op_and_maybe_check_sum(
+ info->db,
+ info->txn,
+ extra,
+ false
+ );
+ assert_zero(r);
+ toku_free(info);
+}
+
+static int UU() scan_op_no_check_parallel(DB_TXN *txn, ARG arg, void* operation_extra, void *UU(stats_extra)) {
+ const int num_cores = toku_os_get_number_processors();
+ const int num_workers = arg->cli->num_DBs < num_cores ? arg->cli->num_DBs : num_cores;
+ KIBBUTZ kibbutz = NULL;
+ int r = toku_kibbutz_create(num_workers, &kibbutz);
+ assert(r == 0);
+ for (int i = 0; run_test && i < arg->cli->num_DBs; i++) {
+ struct scan_op_worker_info *XCALLOC(info);
+ info->db = arg->dbp[i];
+ info->txn = txn;
+ info->extra = operation_extra;
+ toku_kibbutz_enq(kibbutz, scan_op_worker, info);
+ }
+ toku_kibbutz_destroy(kibbutz);
+ return 0;
+}
+
+static int dbt_do_nothing (DBT const *UU(key), DBT const *UU(row), void *UU(context)) {
+ return 0;
+}
+
+static int UU() ptquery_and_maybe_check_op(DB* db, DB_TXN *txn, ARG arg, bool check) {
+ int r = 0;
+ uint8_t keybuf[arg->cli->key_size];
+ DBT key, val;
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, nullptr, 0);
+ fill_key_buf_random(arg->random_data, keybuf, arg);
+
+ r = db->getf_set(
+ db,
+ txn,
+ 0,
+ &key,
+ dbt_do_nothing,
+ nullptr
+ );
+ if (check) {
+ assert(r != DB_NOTFOUND);
+ }
+ r = 0;
+ return r;
+}
+
+static int UU() ptquery_op(DB_TXN *txn, ARG arg, void* UU(operation_extra), void *stats_extra) {
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+ int r = ptquery_and_maybe_check_op(db, txn, arg, true);
+ if (!r) {
+ increment_counter(stats_extra, PTQUERIES, 1);
+ }
+ return r;
+}
+
+static int UU() ptquery_op_no_check(DB_TXN *txn, ARG arg, void* UU(operation_extra), void *stats_extra) {
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+ int r = ptquery_and_maybe_check_op(db, txn, arg, false);
+ if (!r) {
+ increment_counter(stats_extra, PTQUERIES, 1);
+ }
+ return r;
+}
+
+typedef void (*rangequery_row_cb)(DB *db, const DBT *key, const DBT *val, void *extra);
+struct rangequery_cb_extra {
+ int rows_read;
+
+ // Call cb(db, key, value, cb_extra) on up to $limit rows.
+ const int limit;
+ const rangequery_row_cb cb;
+ DB *const db;
+ void *const cb_extra;
+};
+
+static int rangequery_cb(const DBT *key, const DBT *value, void *extra) {
+ struct rangequery_cb_extra *CAST_FROM_VOIDP(info, extra);
+ if (info->cb != nullptr) {
+ info->cb(info->db, key, value, info->cb_extra);
+ }
+ if (++info->rows_read >= info->limit) {
+ return 0;
+ } else {
+ return TOKUDB_CURSOR_CONTINUE;
+ }
+}
+
+static void rangequery_db(DB *db, DB_TXN *txn, ARG arg, rangequery_row_cb cb, void *cb_extra) {
+ const int limit = arg->cli->range_query_limit;
+
+ int r;
+ DBC *cursor;
+ DBT start_key, end_key;
+ uint8_t start_keybuf[arg->cli->key_size];
+ uint8_t end_keybuf[arg->cli->key_size];
+ dbt_init(&start_key, start_keybuf, sizeof start_keybuf);
+ dbt_init(&end_key, end_keybuf, sizeof end_keybuf);
+ const uint64_t start_k = random_bounded_key(arg->random_data, arg);
+ fill_key_buf(start_k, start_keybuf, arg->cli);
+ fill_key_buf(start_k + limit, end_keybuf, arg->cli);
+
+ r = db->cursor(db, txn, &cursor, 0); CKERR(r);
+ r = cursor->c_set_bounds(cursor, &start_key, &end_key, true, 0); CKERR(r);
+
+ struct rangequery_cb_extra extra = {
+ .rows_read = 0,
+ .limit = limit,
+ .cb = cb,
+ .db = db,
+ .cb_extra = cb_extra,
+ };
+ r = cursor->c_getf_set(cursor, 0, &start_key, rangequery_cb, &extra);
+ while (r == 0 && extra.rows_read < extra.limit && run_test) {
+ r = cursor->c_getf_next(cursor, 0, rangequery_cb, &extra);
+ }
+
+ r = cursor->c_close(cursor); CKERR(r);
+}
+
+static int UU() rangequery_op(DB_TXN *txn, ARG arg, void *UU(operation_extra), void *stats_extra) {
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB *db = arg->dbp[db_index];
+ rangequery_db(db, txn, arg, nullptr, nullptr);
+ increment_counter(stats_extra, PTQUERIES, 1);
+ return 0;
+}
+
+static int UU() cursor_create_close_op(DB_TXN *txn, ARG arg, void* UU(operation_extra), void *UU(stats_extra)) {
+ int db_index = arg->cli->num_DBs > 1 ? myrandom_r(arg->random_data)%arg->cli->num_DBs : 0;
+ DB* db = arg->dbp[db_index];
+ DBC* cursor = nullptr;
+ int r = db->cursor(db, txn, &cursor, 0); assert(r == 0);
+ r = cursor->c_close(cursor); assert(r == 0);
+ return 0;
+}
+
+#define MAX_RANDOM_VAL 10000
+
+enum update_type {
+ UPDATE_ADD_DIFF,
+ UPDATE_NEGATE,
+ UPDATE_WITH_HISTORY
+};
+
+struct update_op_extra {
+ enum update_type type;
+ int pad_bytes;
+ union {
+ struct {
+ int diff;
+ } d;
+ struct {
+ int expected;
+ int new_val;
+ } h;
+ } u;
+};
+
+struct update_op_args {
+ int *update_history_buffer;
+ int update_pad_frequency;
+};
+
+static struct update_op_args UU() get_update_op_args(struct cli_args* cli_args, int* update_history_buffer) {
+ struct update_op_args uoe;
+ uoe.update_history_buffer = update_history_buffer;
+ uoe.update_pad_frequency = cli_args->num_elements/100; // arbitrary
+ return uoe;
+}
+
+static uint64_t update_count = 0;
+
+static int update_op_callback(DB *UU(db), const DBT *UU(key),
+ const DBT *old_val,
+ const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra)
+{
+ int old_int_val = 0;
+ if (old_val) {
+ old_int_val = *(int *) old_val->data;
+ }
+ assert(extra->size == sizeof(struct update_op_extra));
+ struct update_op_extra *CAST_FROM_VOIDP(e, extra->data);
+
+ int new_int_val;
+ switch (e->type) {
+ case UPDATE_ADD_DIFF:
+ new_int_val = old_int_val + e->u.d.diff;
+ break;
+ case UPDATE_NEGATE:
+ new_int_val = -old_int_val;
+ break;
+ case UPDATE_WITH_HISTORY:
+ assert(old_int_val == e->u.h.expected);
+ new_int_val = e->u.h.new_val;
+ break;
+ default:
+ abort();
+ }
+
+ uint32_t val_size = sizeof(int) + e->pad_bytes;
+ uint8_t valbuf[val_size];
+ fill_val_buf(new_int_val, valbuf, val_size);
+
+ DBT new_val;
+ dbt_init(&new_val, valbuf, val_size);
+ set_val(&new_val, set_extra);
+ return 0;
+}
+
+static int UU() update_op2(DB_TXN* txn, ARG arg, void* UU(operation_extra), void *UU(stats_extra)) {
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+
+ int r = 0;
+ DBT key, val;
+ uint8_t keybuf[arg->cli->key_size];
+
+ toku_sync_fetch_and_add(&update_count, 1);
+ struct update_op_extra extra;
+ ZERO_STRUCT(extra);
+ extra.type = UPDATE_ADD_DIFF;
+ extra.pad_bytes = 0;
+ int curr_val_sum = 0;
+
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, &extra, sizeof extra);
+
+ for (uint32_t i = 0; i < arg->cli->txn_size; i++) {
+ fill_key_buf_random(arg->random_data, keybuf, arg);
+ extra.u.d.diff = 1;
+ curr_val_sum += extra.u.d.diff;
+ r = db->update(
+ db,
+ txn,
+ &key,
+ &val,
+ 0
+ );
+ if (r != 0) {
+ return r;
+ }
+ int *rkp = (int *) keybuf;
+ int rand_key = *rkp;
+ invariant(rand_key != (arg->cli->num_elements - rand_key));
+ rand_key -= arg->cli->num_elements;
+ fill_key_buf(rand_key, keybuf, arg->cli);
+ extra.u.d.diff = -1;
+ r = db->update(
+ db,
+ txn,
+ &key,
+ &val,
+ 0
+ );
+ if (r != 0) {
+ return r;
+ }
+ }
+ return r;
+}
+
+static int pre_acquire_write_lock(DB *db, DB_TXN *txn,
+ const DBT *left_key, const DBT *right_key) {
+ int r;
+ DBC *cursor;
+
+ r = db->cursor(db, txn, &cursor, DB_RMW);
+ CKERR(r);
+ int cursor_r = cursor->c_set_bounds(cursor, left_key, right_key, true, 0);
+ r = cursor->c_close(cursor);
+ CKERR(r);
+
+ return cursor_r;
+}
+
+// take the given db and do an update on it
+static int
+UU() update_op_db(DB *db, DB_TXN *txn, ARG arg, void* operation_extra, void *UU(stats_extra)) {
+ uint64_t old_update_count = toku_sync_fetch_and_add(&update_count, 1);
+ struct update_op_args* CAST_FROM_VOIDP(op_args, operation_extra);
+ struct update_op_extra extra;
+ ZERO_STRUCT(extra);
+ extra.type = UPDATE_ADD_DIFF;
+ extra.pad_bytes = 0;
+ if (op_args->update_pad_frequency) {
+ if (old_update_count % (2*op_args->update_pad_frequency) == old_update_count%op_args->update_pad_frequency) {
+ extra.pad_bytes = 100;
+ }
+ }
+
+ int r = 0;
+ DBT key, val;
+ uint8_t keybuf[arg->cli->key_size];
+ int update_key;
+ int curr_val_sum = 0;
+ const int update_flags = arg->cli->prelock_updates ? DB_PRELOCKED_WRITE : 0;
+
+ for (uint32_t i = 0; i < arg->cli->txn_size; i++) {
+ if (arg->prelock_updates) {
+ if (i == 0) {
+ update_key = random_bounded_key(arg->random_data, arg);
+
+ const int max_key_in_table = arg->cli->num_elements - 1;
+ const bool range_wraps = (update_key + (int) arg->cli->txn_size - 1) > max_key_in_table;
+ int left_key, right_key;
+ DBT left_key_dbt, right_key_dbt;
+
+ // acquire the range starting at the random key, plus txn_size - 1
+ // elements, but lock no further than the end of the table. if the
+ // range wraps around to the beginning we will handle it below.
+ left_key = update_key;
+ right_key = range_wraps ? max_key_in_table : (left_key + arg->cli->txn_size - 1);
+ r = pre_acquire_write_lock(
+ db,
+ txn,
+ dbt_init(&left_key_dbt, &left_key, sizeof update_key),
+ dbt_init(&right_key_dbt, &right_key, sizeof right_key)
+ );
+ if (r != 0) {
+ return r;
+ }
+
+ // check if the right end point wrapped around to the beginning
+ // if so, lock from 0 to the right key, modded by table size.
+ if (range_wraps) {
+ right_key = (left_key + arg->cli->txn_size - 1) - max_key_in_table;
+ invariant(right_key > 0);
+ left_key = 0;
+ r = pre_acquire_write_lock(
+ db,
+ txn,
+ dbt_init(&left_key_dbt, &left_key, sizeof update_key),
+ dbt_init(&right_key_dbt, &right_key, sizeof right_key)
+ );
+ if (r != 0) {
+ return r;
+ }
+ }
+ } else {
+ update_key++;
+ if (arg->bounded_element_range) {
+ update_key = update_key % arg->cli->num_elements;
+ }
+ }
+ fill_key_buf(update_key, keybuf, arg->cli);
+ } else {
+ // just do a usual, random point update without locking first
+ fill_key_buf_random(arg->random_data, keybuf, arg);
+ }
+
+
+ // the last update keeps the table's sum as zero
+ // every other update except the last applies a random delta
+ if (i == arg->cli->txn_size - 1) {
+ extra.u.d.diff = -curr_val_sum;
+ } else {
+ extra.u.d.diff = myrandom_r(arg->random_data) % MAX_RANDOM_VAL;
+ // just make every other value random
+ if (i%2 == 0) {
+ extra.u.d.diff = -extra.u.d.diff;
+ }
+ curr_val_sum += extra.u.d.diff;
+ }
+
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, &extra, sizeof extra);
+
+ // do the update
+ r = db->update(
+ db,
+ txn,
+ &key,
+ &val,
+ update_flags
+ );
+ if (r != 0) {
+ return r;
+ }
+ }
+
+ return r;
+}
+
+// choose a random DB and do an update on it
+static int
+UU() update_op(DB_TXN *txn, ARG arg, void* operation_extra, void *stats_extra) {
+ int db_index = myrandom_r(arg->random_data) % arg->cli->num_DBs;
+ DB *db = arg->dbp[db_index];
+ return update_op_db(db, txn, arg, operation_extra, stats_extra);
+}
+
+static int UU() update_with_history_op(DB_TXN *txn, ARG arg, void* operation_extra, void *UU(stats_extra)) {
+ struct update_op_args* CAST_FROM_VOIDP(op_args, operation_extra);
+ assert(arg->bounded_element_range);
+ assert(op_args->update_history_buffer);
+
+ int r = 0;
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+
+ struct update_op_extra extra;
+ ZERO_STRUCT(extra);
+ extra.type = UPDATE_WITH_HISTORY;
+ uint64_t old_update_count = toku_sync_fetch_and_add(&update_count, 1);
+ extra.pad_bytes = 0;
+ if (op_args->update_pad_frequency) {
+ if (old_update_count % (2*op_args->update_pad_frequency) != old_update_count%op_args->update_pad_frequency) {
+ extra.pad_bytes = 500;
+ }
+ }
+
+ DBT key, val;
+ uint8_t keybuf[arg->cli->key_size];
+ int rand_key;
+ int curr_val_sum = 0;
+
+ dbt_init(&key, keybuf, sizeof keybuf);
+ dbt_init(&val, &extra, sizeof extra);
+
+ for (uint32_t i = 0; i < arg->cli->txn_size; i++) {
+ fill_key_buf_random(arg->random_data, keybuf, arg);
+ int *rkp = (int *) keybuf;
+ rand_key = *rkp;
+ invariant(rand_key < arg->cli->num_elements);
+ if (i < arg->cli->txn_size - 1) {
+ extra.u.h.new_val = myrandom_r(arg->random_data) % MAX_RANDOM_VAL;
+ // just make every other value random
+ if (i % 2 == 0) {
+ extra.u.h.new_val = -extra.u.h.new_val;
+ }
+ curr_val_sum += extra.u.h.new_val;
+ } else {
+ // the last update should ensure the sum stays zero
+ extra.u.h.new_val = -curr_val_sum;
+ }
+ extra.u.h.expected = op_args->update_history_buffer[rand_key];
+ op_args->update_history_buffer[rand_key] = extra.u.h.new_val;
+ r = db->update(
+ db,
+ txn,
+ &key,
+ &val,
+ 0
+ );
+ if (r != 0) {
+ return r;
+ }
+ }
+
+ return r;
+}
+
+static int UU() update_broadcast_op(DB_TXN *txn, ARG arg, void* UU(operation_extra), void *UU(stats_extra)) {
+ struct update_op_extra extra;
+ ZERO_STRUCT(extra);
+ int db_index = myrandom_r(arg->random_data)%arg->cli->num_DBs;
+ DB* db = arg->dbp[db_index];
+ extra.type = UPDATE_NEGATE;
+ extra.pad_bytes = 0;
+ DBT val;
+ int r = db->update_broadcast(db, txn, dbt_init(&val, &extra, sizeof extra), 0);
+ CKERR(r);
+ return r;
+}
+
+static int hot_progress_callback(void *UU(extra), float UU(progress)) {
+ return run_test ? 0 : 1;
+}
+
+static int UU() hot_op(DB_TXN *UU(txn), ARG UU(arg), void* UU(operation_extra), void *UU(stats_extra)) {
+ int r;
+ for (int i = 0; run_test && i < arg->cli->num_DBs; i++) {
+ DB* db = arg->dbp[i];
+ uint64_t loops_run;
+ r = db->hot_optimize(db, NULL, NULL, hot_progress_callback, nullptr, &loops_run);
+ if (run_test) {
+ CKERR(r);
+ }
+ }
+ return 0;
+}
+
+static void
+get_ith_table_name(char *buf, size_t len, int i) {
+ snprintf(buf, len, "main%d", i);
+}
+
+DB_TXN * const null_txn = 0;
+
+// For each line of engine status output, look for lines that contain substrings
+// that match any of the strings in the pattern string. The pattern string contains
+// 0 or more strings separated by the '|' character, kind of like a regex.
+static void print_matching_engine_status_rows(DB_ENV *env, const char *pattern) {
+ uint64_t num_rows;
+ env->get_engine_status_num_rows(env, &num_rows);
+ uint64_t buf_size = num_rows * 128;
+ const char *row;
+ char *row_r;
+
+ char *pattern_copy = toku_xstrdup(pattern);
+ int num_patterns = 1;
+ for (char *p = pattern_copy; *p != '\0'; p++) {
+ if (*p == '|') {
+ *p = '\0';
+ num_patterns++;
+ }
+ }
+
+ char *XMALLOC_N(buf_size, buf);
+ int r = env->get_engine_status_text(env, buf, buf_size);
+ invariant_zero(r);
+
+ for (row = strtok_r(buf, "\n", &row_r); row != nullptr; row = strtok_r(nullptr, "\n", &row_r)) {
+ const char *p = pattern_copy;
+ for (int i = 0; i < num_patterns; i++, p += strlen(p) + 1) {
+ if (strstr(row, p) != nullptr) {
+ fprintf(stderr, "%s\n", row);
+ }
+ }
+ }
+
+ toku_free(pattern_copy);
+ toku_free(buf);
+ fflush(stderr);
+}
+
+// TODO: stuff like this should be in a generalized header somwhere
+static inline int
+intmin(const int a, const int b)
+{
+ if (a < b) {
+ return a;
+ }
+ return b;
+}
+
+struct test_time_extra {
+ DB_ENV *env;
+ int num_seconds;
+ bool crash_at_end;
+ struct worker_extra *wes;
+ int num_wes;
+ struct cli_args *cli_args;
+};
+
+static void *test_time(void *arg) {
+ struct test_time_extra* CAST_FROM_VOIDP(tte, arg);
+ DB_ENV *env = tte->env;
+ int num_seconds = tte->num_seconds;
+ const struct perf_formatter *perf_formatter = &perf_formatters[tte->cli_args->perf_output_format];
+
+ //
+ // if num_Seconds is set to 0, run indefinitely
+ //
+ if (num_seconds == 0) {
+ num_seconds = INT32_MAX;
+ }
+ uint64_t last_counter_values[tte->num_wes][(int) NUM_OPERATION_TYPES];
+ ZERO_ARRAY(last_counter_values);
+ uint64_t *counters[tte->num_wes];
+ for (int t = 0; t < tte->num_wes; ++t) {
+ counters[t] = tte->wes[t].counters;
+ }
+ if (verbose) {
+ printf("Sleeping for %d seconds\n", num_seconds);
+ }
+ for (int i = 0; i < num_seconds; ) {
+ struct timeval tv[2];
+ const int sleeptime = intmin(tte->cli_args->performance_period, num_seconds - i);
+ int r = gettimeofday(&tv[0], nullptr);
+ assert_zero(r);
+ usleep(sleeptime*1000*1000);
+ r = gettimeofday(&tv[1], nullptr);
+ assert_zero(r);
+ int actual_sleeptime = tv[1].tv_sec - tv[0].tv_sec;
+ if (abs(actual_sleeptime - sleeptime) <= 1) {
+ // Close enough, no need to alarm the user, and we didn't check nsec.
+ i += sleeptime;
+ } else {
+ if (verbose) {
+ printf("tried to sleep %d secs, actually slept %d secs\n", sleeptime, actual_sleeptime);
+ }
+ i += actual_sleeptime;
+ }
+ if (tte->cli_args->print_performance && tte->cli_args->print_iteration_performance) {
+ perf_formatter->iteration(tte->cli_args, i, last_counter_values, counters, tte->num_wes);
+ }
+ if (tte->cli_args->print_engine_status != nullptr) {
+ print_matching_engine_status_rows(env, tte->cli_args->print_engine_status);
+ }
+ }
+
+ if (verbose) {
+ printf("should now end test\n");
+ }
+ toku_sync_bool_compare_and_swap(&run_test, true, false); // make this atomic to make valgrind --tool=drd happy.
+ if (verbose) {
+ printf("run_test %d\n", run_test);
+ }
+ if (tte->crash_at_end) {
+ toku_hard_crash_on_purpose();
+ }
+ return arg;
+}
+
+struct sleep_and_crash_extra {
+ toku_mutex_t mutex;
+ toku_cond_t cond;
+ int seconds;
+ bool is_setup;
+ bool threads_have_joined;
+};
+
+static void *sleep_and_crash(void *extra) {
+ sleep_and_crash_extra *e = static_cast<sleep_and_crash_extra *>(extra);
+ toku_mutex_lock(&e->mutex);
+ struct timeval tv;
+ toku_timespec_t ts;
+ gettimeofday(&tv, nullptr);
+ ts.tv_sec = tv.tv_sec + e->seconds;
+ ts.tv_nsec = 0;
+ e->is_setup = true;
+ if (verbose) {
+ printf("Waiting %d seconds for other threads to join.\n", e->seconds);
+ fflush(stdout);
+ }
+ int r = toku_cond_timedwait(&e->cond, &e->mutex, &ts);
+ toku_mutex_assert_locked(&e->mutex);
+ if (r == ETIMEDOUT) {
+ invariant(!e->threads_have_joined);
+ if (verbose) {
+ printf("Some thread didn't join on time, crashing.\n");
+ fflush(stdout);
+ }
+ toku_crash_and_dump_core_on_purpose();
+ } else {
+ assert(r == 0);
+ assert(e->threads_have_joined);
+ if (verbose) {
+ printf("Other threads joined on time, exiting cleanly.\n");
+ }
+ }
+ toku_mutex_unlock(&e->mutex);
+ return nullptr;
+}
+
+static int run_workers(
+ struct arg *thread_args,
+ int num_threads,
+ uint32_t num_seconds,
+ bool crash_at_end,
+ struct cli_args* cli_args
+ )
+{
+ int r;
+ const struct perf_formatter *perf_formatter = &perf_formatters[cli_args->perf_output_format];
+ toku_mutex_t mutex = ZERO_MUTEX_INITIALIZER;
+ toku_mutex_init(&mutex, nullptr);
+ struct rwlock rwlock;
+ rwlock_init(&rwlock);
+ toku_pthread_t tids[num_threads];
+ toku_pthread_t time_tid;
+ if (cli_args->print_performance) {
+ perf_formatter->header(cli_args, num_threads);
+ }
+ // allocate worker_extra's on cache line boundaries
+ struct worker_extra *XMALLOC_N_ALIGNED(64, num_threads, worker_extra);
+ struct test_time_extra tte;
+ tte.env = thread_args[0].env;
+ tte.num_seconds = num_seconds;
+ tte.crash_at_end = crash_at_end;
+ tte.wes = worker_extra;
+ tte.num_wes = num_threads;
+ tte.cli_args = cli_args;
+ run_test = true;
+ for (int i = 0; i < num_threads; ++i) {
+ thread_args[i].thread_idx = i;
+ thread_args[i].num_threads = num_threads;
+ worker_extra[i].thread_arg = &thread_args[i];
+ worker_extra[i].operation_lock = &rwlock;
+ worker_extra[i].operation_lock_mutex = &mutex;
+ XCALLOC_N((int) NUM_OPERATION_TYPES, worker_extra[i].counters);
+ TOKU_DRD_IGNORE_VAR(worker_extra[i].counters);
+ { int chk_r = toku_pthread_create(&tids[i], nullptr, worker, &worker_extra[i]); CKERR(chk_r); }
+ if (verbose)
+ printf("%lu created\n", (unsigned long) tids[i]);
+ }
+ { int chk_r = toku_pthread_create(&time_tid, nullptr, test_time, &tte); CKERR(chk_r); }
+ if (verbose)
+ printf("%lu created\n", (unsigned long) time_tid);
+
+ void *ret;
+ r = toku_pthread_join(time_tid, &ret); assert_zero(r);
+ if (verbose) printf("%lu joined\n", (unsigned long) time_tid);
+
+ {
+ // Set an alarm that will kill us if it takes too long to join all the
+ // threads (i.e. there is some runaway thread).
+ struct sleep_and_crash_extra sac_extra;
+ ZERO_STRUCT(sac_extra);
+ toku_mutex_init(&sac_extra.mutex, nullptr);
+ toku_cond_init(&sac_extra.cond, nullptr);
+ sac_extra.seconds = cli_args->join_timeout;
+ sac_extra.is_setup = false;
+ sac_extra.threads_have_joined = false;
+
+ toku_mutex_lock(&sac_extra.mutex);
+ toku_pthread_t sac_thread;
+ r = toku_pthread_create(&sac_thread, nullptr, sleep_and_crash, &sac_extra);
+ assert_zero(r);
+ // Wait for sleep_and_crash thread to get set up, spinning is ok, this should be quick.
+ while (!sac_extra.is_setup) {
+ toku_mutex_unlock(&sac_extra.mutex);
+ r = toku_pthread_yield();
+ assert_zero(r);
+ toku_mutex_lock(&sac_extra.mutex);
+ }
+ toku_mutex_unlock(&sac_extra.mutex);
+
+ // Timeout thread has started, join everyone
+ for (int i = 0; i < num_threads; ++i) {
+ r = toku_pthread_join(tids[i], &ret); assert_zero(r);
+ if (verbose)
+ printf("%lu joined\n", (unsigned long) tids[i]);
+ }
+
+ // Signal timeout thread not to crash.
+ toku_mutex_lock(&sac_extra.mutex);
+ sac_extra.threads_have_joined = true;
+ toku_cond_signal(&sac_extra.cond);
+ toku_mutex_unlock(&sac_extra.mutex);
+ r = toku_pthread_join(sac_thread, nullptr);
+ assert_zero(r);
+ toku_cond_destroy(&sac_extra.cond);
+ toku_mutex_destroy(&sac_extra.mutex);
+ }
+
+ if (cli_args->print_performance) {
+ uint64_t *counters[num_threads];
+ for (int i = 0; i < num_threads; ++i) {
+ counters[i] = worker_extra[i].counters;
+ }
+ perf_formatter->totals(cli_args, counters, num_threads);
+ }
+
+ for (int i = 0; i < num_threads; ++i) {
+ toku_free(worker_extra[i].counters);
+ }
+ if (verbose)
+ printf("ending test, pthreads have joined\n");
+ rwlock_destroy(&rwlock);
+ toku_mutex_destroy(&mutex);
+ toku_free(worker_extra);
+ return r;
+}
+
+// Pre-open hook
+static void do_nothing_before_db_open(DB *UU(db), int UU(idx)) { }
+// Requires: DB is created (allocated) but not opened. idx is the index
+// into the DBs array.
+static void (*before_db_open_hook)(DB *db, int idx) = do_nothing_before_db_open;
+
+// Post-open hook
+typedef void (*reopen_db_fn)(DB *db, int idx, struct cli_args *cli_args);
+static DB *do_nothing_after_db_open(DB_ENV *UU(env), DB *db, int UU(idx), reopen_db_fn UU(reopen), struct cli_args *UU(cli_args)) { return db; }
+// Requires: DB is opened and is the 'idx' db in the DBs array.
+// Note: Reopen function may be used to open a db if the given one was closed.
+// Returns: An opened db.
+static DB *(*after_db_open_hook)(DB_ENV *env, DB *db, int idx, reopen_db_fn reopen, struct cli_args *cli_args) = do_nothing_after_db_open;
+
+static void open_db_for_create(DB *db, int idx, struct cli_args *cli_args) {
+ int r;
+ char name[30];
+ memset(name, 0, sizeof(name));
+ get_ith_table_name(name, sizeof(name), idx);
+ r = db->set_flags(db, 0); CKERR(r);
+ r = db->set_fanout(db, cli_args->env_args.fanout); CKERR(r);
+ r = db->set_pagesize(db, cli_args->env_args.node_size); CKERR(r);
+ r = db->set_readpagesize(db, cli_args->env_args.basement_node_size); CKERR(r);
+ r = db->set_compression_method(db, cli_args->compression_method); CKERR(r);
+ const int flags = DB_CREATE | (cli_args->blackhole ? DB_BLACKHOLE : 0);
+ r = db->open(db, null_txn, name, nullptr, DB_BTREE, flags, 0666); CKERR(r);
+}
+
+static void open_db(DB *db, int idx, struct cli_args *cli_args) {
+ int r;
+ char name[30];
+ memset(name, 0, sizeof(name));
+ get_ith_table_name(name, sizeof(name), idx);
+ const int flags = DB_CREATE | (cli_args->blackhole ? DB_BLACKHOLE : 0);
+ r = db->open(db, null_txn, name, nullptr, DB_BTREE, flags, 0666); CKERR(r);
+ r = db->change_fanout(db, cli_args->env_args.fanout); CKERR(r); // change fanout until fanout is persistent
+}
+
+static int create_tables(DB_ENV **env_res, DB **db_res, int num_DBs,
+ int (*bt_compare)(DB *, const DBT *, const DBT *),
+ struct cli_args *cli_args
+) {
+ int r;
+ struct env_args env_args = cli_args->env_args;
+
+ char rmcmd[32 + strlen(env_args.envdir)]; sprintf(rmcmd, "rm -rf %s", env_args.envdir);
+ r = system(rmcmd);
+ CKERR(r);
+ r = toku_os_mkdir(env_args.envdir, S_IRWXU+S_IRWXG+S_IRWXO); assert(r==0);
+
+ DB_ENV *env;
+ db_env_set_num_bucket_mutexes(env_args.num_bucket_mutexes);
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->set_redzone(env, 0); CKERR(r);
+ if (!cli_args->memcmp_keys) {
+ r = env->set_default_bt_compare(env, bt_compare); CKERR(r);
+ }
+ r = env->set_lk_max_memory(env, env_args.lk_max_memory); CKERR(r);
+ r = env->set_cachesize(env, env_args.cachetable_size / (1 << 30), env_args.cachetable_size % (1 << 30), 1); CKERR(r);
+ r = env->set_lg_bsize(env, env_args.rollback_node_size); CKERR(r);
+ if (env_args.generate_put_callback) {
+ r = env->set_generate_row_callback_for_put(env, env_args.generate_put_callback);
+ CKERR(r);
+ }
+ else {
+ r = env->set_generate_row_callback_for_put(env, generate_row_for_put);
+ CKERR(r);
+ }
+ if (env_args.generate_del_callback) {
+ r = env->set_generate_row_callback_for_del(env, env_args.generate_del_callback);
+ CKERR(r);
+ }
+ int env_flags = get_env_open_flags(cli_args);
+ r = env->open(env, env_args.envdir, env_flags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = env->checkpointing_set_period(env, env_args.checkpointing_period); CKERR(r);
+ r = env->cleaner_set_period(env, env_args.cleaner_period); CKERR(r);
+ r = env->cleaner_set_iterations(env, env_args.cleaner_iterations); CKERR(r);
+ env->change_fsync_log_period(env, env_args.sync_period);
+ *env_res = env;
+
+ for (int i = 0; i < num_DBs; i++) {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ before_db_open_hook(db, i);
+ open_db_for_create(db, i, cli_args);
+ db_res[i] = after_db_open_hook(env, db, i, open_db_for_create, cli_args);
+ }
+ return r;
+}
+
+static void report_overall_fill_table_progress(struct cli_args *args, int num_rows) {
+ // for sanitary reasons we'd like to prevent two threads
+ // from printing the same performance report twice.
+ static bool reporting;
+
+ // when was the first time measurement taken?
+ static uint64_t t0;
+ static int rows_inserted;
+
+ // when was the last report? what was its progress?
+ static uint64_t last_report;
+ static double last_progress;
+ if (t0 == 0) {
+ t0 = toku_current_time_microsec();
+ last_report = t0;
+ }
+
+ uint64_t rows_so_far = toku_sync_add_and_fetch(&rows_inserted, num_rows);
+ double progress = rows_so_far / (args->num_elements * args->num_DBs * 1.0);
+ if (progress > (last_progress + .01)) {
+ uint64_t t1 = toku_current_time_microsec();
+ const uint64_t minimum_report_period = 5 * 1000000;
+ if (t1 > last_report + minimum_report_period
+ && toku_sync_bool_compare_and_swap(&reporting, 0, 1) == 0) {
+ double inserts_per_sec = (rows_so_far*1000000) / ((t1 - t0) * 1.0);
+ printf("fill tables: %ld%% complete, %.2lf rows/sec\n",
+ (long)(progress * 100), inserts_per_sec);
+ last_progress = progress;
+ last_report = t1;
+ reporting = false;
+ }
+ }
+}
+
+static void fill_single_table(DB_ENV *env, DB *db, struct cli_args *args, bool fill_with_zeroes) {
+ const int min_size_for_loader = 1 * 1000 * 1000;
+ const int puts_per_txn = 10 * 1000;;
+
+ int r = 0;
+ DB_TXN *txn = nullptr;
+ DB_LOADER *loader = nullptr;
+ struct random_data random_data;
+ char random_buf[8];
+ memset(&random_data, 0, sizeof(random_data));
+ memset(random_buf, 0, 8);
+ r = myinitstate_r(random(), random_buf, 8, &random_data); CKERR(r);
+
+ uint8_t keybuf[args->key_size], valbuf[args->val_size];
+ memset(keybuf, 0, sizeof keybuf);
+ memset(valbuf, 0, sizeof valbuf);
+ DBT key, val;
+ dbt_init(&key, keybuf, args->key_size);
+ dbt_init(&val, valbuf, args->val_size);
+
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ if (args->num_elements >= min_size_for_loader) {
+ uint32_t db_flags = DB_PRELOCKED_WRITE;
+ uint32_t dbt_flags = 0;
+ r = env->create_loader(env, txn, &loader, db, 1, &db, &db_flags, &dbt_flags, 0); CKERR(r);
+ }
+
+ for (int i = 0; i < args->num_elements; i++) {
+ fill_key_buf(i, keybuf, args);
+
+ // Correctness tests map every key to zeroes. Perf tests fill
+ // values with random bytes, based on compressibility.
+ if (fill_with_zeroes) {
+ fill_val_buf(0, valbuf, args->val_size);
+ } else {
+ fill_val_buf_random(&random_data, valbuf, args);
+ }
+
+ r = loader ? loader->put(loader, &key, &val) :
+ db->put(db, txn, &key, &val, DB_PRELOCKED_WRITE);
+ CKERR(r);
+
+ if (i > 0 && i % puts_per_txn == 0) {
+ if (verbose) {
+ report_overall_fill_table_progress(args, puts_per_txn);
+ }
+ // begin a new txn if we're not using the loader,
+ if (loader == nullptr) {
+ r = txn->commit(txn, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ }
+ }
+ }
+
+ if (loader) {
+ r = loader->close(loader); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+}
+
+struct fill_table_worker_info {
+ struct cli_args *args;
+ DB_ENV *env;
+ DB *db;
+ bool fill_with_zeroes;
+};
+
+static void fill_table_worker(void *arg) {
+ struct fill_table_worker_info *CAST_FROM_VOIDP(info, arg);
+ fill_single_table(info->env, info->db, info->args, info->fill_with_zeroes);
+ toku_free(info);
+}
+
+static int fill_tables_default(DB_ENV *env, DB **dbs, struct cli_args *args, bool fill_with_zeroes) {
+ const int num_cores = toku_os_get_number_processors();
+ // Use at most cores / 2 worker threads, since we want some other cores to
+ // be used for internal engine work (ie: flushes, loader threads, etc).
+ const int max_num_workers = (num_cores + 1) / 2;
+ const int num_workers = args->num_DBs < max_num_workers ? args->num_DBs : max_num_workers;
+ KIBBUTZ kibbutz = NULL;
+ int r = toku_kibbutz_create(num_workers, &kibbutz);
+ assert(r == 0);
+ for (int i = 0; i < args->num_DBs; i++) {
+ struct fill_table_worker_info *XCALLOC(info);
+ info->env = env;
+ info->db = dbs[i];
+ info->args = args;
+ info->fill_with_zeroes = fill_with_zeroes;
+ toku_kibbutz_enq(kibbutz, fill_table_worker, info);
+ }
+ toku_kibbutz_destroy(kibbutz);
+ return 0;
+}
+
+// fill_tables() is called when the tables are first created.
+// set this function if you want custom table contents.
+static int (*fill_tables)(DB_ENV *env, DB **dbs, struct cli_args *args, bool fill_with_zeroes) = fill_tables_default;
+
+static void do_xa_recovery(DB_ENV* env) {
+ DB_PREPLIST preplist[1];
+ long num_recovered= 0;
+ int r = 0;
+ r = env->txn_recover(env, preplist, 1, &num_recovered, DB_NEXT);
+ while(r==0 && num_recovered > 0) {
+ DB_TXN* recovered_txn = preplist[0].txn;
+ if (verbose) {
+ printf("recovering transaction with id %" PRIu64 " \n", recovered_txn->id64(recovered_txn));
+ }
+ if (random() % 2 == 0) {
+ int rr = recovered_txn->commit(recovered_txn, 0);
+ CKERR(rr);
+ }
+ else {
+ int rr = recovered_txn->abort(recovered_txn);
+ CKERR(rr);
+ }
+ r = env->txn_recover(env, preplist, 1, &num_recovered, DB_NEXT);
+ }
+}
+
+static int open_tables(DB_ENV **env_res, DB **db_res, int num_DBs,
+ int (*bt_compare)(DB *, const DBT *, const DBT *),
+ struct cli_args *cli_args) {
+ int r;
+ struct env_args env_args = cli_args->env_args;
+
+ DB_ENV *env;
+ db_env_set_num_bucket_mutexes(env_args.num_bucket_mutexes);
+ r = db_env_create(&env, 0); assert(r == 0);
+ r = env->set_redzone(env, 0); CKERR(r);
+ if (!cli_args->memcmp_keys) {
+ r = env->set_default_bt_compare(env, bt_compare); CKERR(r);
+ }
+ r = env->set_lk_max_memory(env, env_args.lk_max_memory); CKERR(r);
+ env->set_update(env, env_args.update_function);
+ r = env->set_cachesize(env, env_args.cachetable_size / (1 << 30), env_args.cachetable_size % (1 << 30), 1); CKERR(r);
+ r = env->set_lg_bsize(env, env_args.rollback_node_size); CKERR(r);
+ if (env_args.generate_put_callback) {
+ r = env->set_generate_row_callback_for_put(env, env_args.generate_put_callback);
+ CKERR(r);
+ }
+ else {
+ r = env->set_generate_row_callback_for_put(env, generate_row_for_put);
+ CKERR(r);
+ }
+ if (env_args.generate_del_callback) {
+ r = env->set_generate_row_callback_for_del(env, env_args.generate_del_callback);
+ CKERR(r);
+ }
+ int env_flags = get_env_open_flags(cli_args);
+ r = env->open(env, env_args.envdir, DB_RECOVER | env_flags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ do_xa_recovery(env);
+ r = env->checkpointing_set_period(env, env_args.checkpointing_period); CKERR(r);
+ r = env->cleaner_set_period(env, env_args.cleaner_period); CKERR(r);
+ r = env->cleaner_set_iterations(env, env_args.cleaner_iterations); CKERR(r);
+ env->change_fsync_log_period(env, env_args.sync_period);
+ *env_res = env;
+
+ for (int i = 0; i < num_DBs; i++) {
+ DB *db;
+ r = db_create(&db, env, 0); CKERR(r);
+ before_db_open_hook(db, i);
+ open_db(db, i, cli_args);
+ db_res[i] = after_db_open_hook(env, db, i, open_db, cli_args);
+ }
+ return r;
+}
+
+static int close_tables(DB_ENV *env, DB** dbs, int num_DBs) {
+ int r;
+ for (int i = 0; i < num_DBs; i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ }
+ r = env->close(env, 0); CKERR(r);
+ return r;
+}
+
+static const struct env_args DEFAULT_ENV_ARGS = {
+ .fanout = 16,
+ .node_size = 4096,
+ .basement_node_size = 1024,
+ .rollback_node_size = 4096,
+ .checkpointing_period = 10,
+ .cleaner_period = 1,
+ .cleaner_iterations = 1,
+ .sync_period = 0,
+ .lk_max_memory = 1L * 1024 * 1024 * 1024,
+ .cachetable_size = 300000,
+ .num_bucket_mutexes = 1024,
+ .envdir = nullptr,
+ .update_function = update_op_callback,
+ .generate_put_callback = nullptr,
+ .generate_del_callback = nullptr,
+};
+
+static const struct env_args DEFAULT_PERF_ENV_ARGS = {
+ .fanout = 16,
+ .node_size = 4*1024*1024,
+ .basement_node_size = 128*1024,
+ .rollback_node_size = 4*1024*1024,
+ .checkpointing_period = 60,
+ .cleaner_period = 1,
+ .cleaner_iterations = 5,
+ .sync_period = 0,
+ .lk_max_memory = 1L * 1024 * 1024 * 1024,
+ .cachetable_size = 1<<30,
+ .num_bucket_mutexes = 1024 * 1024,
+ .envdir = nullptr,
+ .update_function = nullptr,
+ .generate_put_callback = nullptr,
+ .generate_del_callback = nullptr,
+};
+
+static struct cli_args UU() get_default_args(void) {
+ struct cli_args DEFAULT_ARGS = {
+ .num_elements = 150000,
+ .num_DBs = 1,
+ .num_seconds = 180,
+ .join_timeout = 3600,
+ .only_create = false,
+ .only_stress = false,
+ .update_broadcast_period_ms = 2000,
+ .num_ptquery_threads = 1,
+ .do_test_and_crash = false,
+ .do_recover = false,
+ .num_update_threads = 1,
+ .num_put_threads = 1,
+ .range_query_limit = 100,
+ .serial_insert = false,
+ .interleave = false,
+ .crash_on_operation_failure = true,
+ .print_performance = false,
+ .print_thread_performance = true,
+ .print_iteration_performance = true,
+ .perf_output_format = HUMAN,
+ .compression_method = TOKU_DEFAULT_COMPRESSION_METHOD,
+ .performance_period = 1,
+ .txn_size = 1000,
+ .key_size = min_key_size,
+ .val_size = min_val_size,
+ .compressibility = 1.0,
+ .env_args = DEFAULT_ENV_ARGS,
+ .single_txn = false,
+ .warm_cache = false,
+ .blackhole = false,
+ .nolocktree = false,
+ .unique_checks = false,
+ .sync_period = 0,
+ .nolog = false,
+ .nocrashstatus = false,
+ .prelock_updates = false,
+ .disperse_keys = false,
+ .memcmp_keys = false,
+ .direct_io = false,
+ };
+ DEFAULT_ARGS.env_args.envdir = TOKU_TEST_FILENAME;
+ return DEFAULT_ARGS;
+}
+
+static struct cli_args UU() get_default_args_for_perf(void) {
+ struct cli_args args = get_default_args();
+ args.num_elements = 1000000; //default of 1M
+ args.env_args = DEFAULT_PERF_ENV_ARGS;
+ args.env_args.envdir = TOKU_TEST_FILENAME;
+ return args;
+}
+
+union val_type {
+ int32_t i32;
+ int64_t i64;
+ uint32_t u32;
+ uint64_t u64;
+ bool b;
+ double d;
+ const char *s;
+};
+
+struct arg_type;
+
+typedef bool (*match_fun)(struct arg_type *type, char *const argv[]);
+typedef int (*parse_fun)(struct arg_type *type, int *extra_args_consumed, int argc, char *const argv[]);
+typedef void (*help_fun)(struct arg_type *type, int width_name, int width_type);
+
+struct type_description {
+ const char *type_name;
+ const match_fun matches;
+ const parse_fun parse;
+ const help_fun help;
+};
+
+struct arg_type {
+ const char *name;
+ struct type_description *description;
+ union val_type default_val;
+ void *target;
+ const char *help_suffix;
+ union val_type min;
+ union val_type max;
+};
+
+#define DEFINE_NUMERIC_HELP(typename, format, member, MIN, MAX) \
+static inline void \
+help_##typename(struct arg_type *type, int width_name, int width_type) { \
+ invariant(!strncmp("--", type->name, strlen("--"))); \
+ fprintf(stderr, "\t%-*s %-*s ", width_name, type->name, width_type, type->description->type_name); \
+ fprintf(stderr, "(default %" format "%s", type->default_val.member, type->help_suffix); \
+ if (type->min.member != MIN) { \
+ fprintf(stderr, ", min %" format "%s", type->min.member, type->help_suffix); \
+ } \
+ if (type->max.member != MAX) { \
+ fprintf(stderr, ", max %" format "%s", type->max.member, type->help_suffix); \
+ } \
+ fprintf(stderr, ")\n"); \
+}
+
+DEFINE_NUMERIC_HELP(int32, PRId32, i32, INT32_MIN, INT32_MAX)
+DEFINE_NUMERIC_HELP(int64, PRId64, i64, INT64_MIN, INT64_MAX)
+DEFINE_NUMERIC_HELP(uint32, PRIu32, u32, 0, UINT32_MAX)
+DEFINE_NUMERIC_HELP(uint64, PRIu64, u64, 0, UINT64_MAX)
+DEFINE_NUMERIC_HELP(double, ".2lf", d, -HUGE_VAL, HUGE_VAL)
+static inline void
+help_bool(struct arg_type *type, int width_name, int width_type) {
+ invariant(strncmp("--", type->name, strlen("--")));
+ const char *default_value = type->default_val.b ? "yes" : "no";
+ fprintf(stderr, "\t--[no-]%-*s %-*s (default %s)\n",
+ width_name - (int)strlen("--[no-]"), type->name,
+ width_type, type->description->type_name,
+ default_value);
+}
+
+static inline void
+help_string(struct arg_type *type, int width_name, int width_type) {
+ invariant(!strncmp("--", type->name, strlen("--")));
+ const char *default_value = type->default_val.s ? type->default_val.s : "";
+ fprintf(stderr, "\t%-*s %-*s (default '%s')\n",
+ width_name, type->name,
+ width_type, type->description->type_name,
+ default_value);
+}
+
+static inline bool
+match_name(struct arg_type *type, char *const argv[]) {
+ invariant(!strncmp("--", type->name, strlen("--")));
+ return !strcmp(argv[1], type->name);
+}
+
+static inline bool
+match_bool(struct arg_type *type, char *const argv[]) {
+ invariant(strncmp("--", type->name, strlen("--")));
+ const char *string = argv[1];
+ if (strncmp(string, "--", strlen("--"))) {
+ return false;
+ }
+ string += strlen("--");
+ if (!strncmp(string, "no-", strlen("no-"))) {
+ string += strlen("no-");
+ }
+ return !strcmp(string, type->name);
+}
+
+static inline int
+parse_bool(struct arg_type *type, int *extra_args_consumed, int UU(argc), char *const argv[]) {
+ const char *string = argv[1];
+ if (!strncmp(string, "--no-", strlen("--no-"))) {
+ *((bool *)type->target) = false;
+ }
+ else {
+ *((bool *)type->target) = true;
+ }
+ *extra_args_consumed = 0;
+ return 0;
+}
+
+static inline int
+parse_string(struct arg_type *type, int *extra_args_consumed, int argc, char *const argv[]) {
+ if (argc < 2) {
+ return EINVAL;
+ }
+ *((const char **)type->target) = argv[2];
+ *extra_args_consumed = 1;
+ return 0;
+}
+
+static inline int
+parse_uint64(struct arg_type *type, int *extra_args_consumed, int argc, char *const argv[]) {
+ // Already verified name.
+
+ if (argc < 2) {
+ return EINVAL;
+ }
+ if (*argv[2] == '\0') {
+ return EINVAL;
+ }
+
+ char *endptr;
+ unsigned long long int result = strtoull(argv[2], &endptr, 0);
+ if (*endptr != '\0') {
+ return EINVAL;
+ }
+ if (result < type->min.u64 || result > type->max.u64) {
+ return ERANGE;
+ }
+ *((uint64_t*)type->target) = result;
+ *extra_args_consumed = 1;
+ return 0;
+}
+
+static inline int
+parse_int64(struct arg_type *type, int *extra_args_consumed, int argc, char *const argv[]) {
+ // Already verified name.
+
+ if (argc < 2) {
+ return EINVAL;
+ }
+ if (*argv[2] == '\0') {
+ return EINVAL;
+ }
+
+ char *endptr;
+ long long int result = strtoll(argv[2], &endptr, 0);
+ if (*endptr != '\0') {
+ return EINVAL;
+ }
+ if (result < type->min.i64 || result > type->max.i64) {
+ return ERANGE;
+ }
+ *((int64_t*)type->target) = result;
+ *extra_args_consumed = 1;
+ return 0;
+}
+
+static inline int
+parse_uint32(struct arg_type *type, int *extra_args_consumed, int argc, char *const argv[]) {
+ // Already verified name.
+
+ if (argc < 2) {
+ return EINVAL;
+ }
+ if (*argv[2] == '\0') {
+ return EINVAL;
+ }
+
+ char *endptr;
+ unsigned long int result = strtoul(argv[2], &endptr, 0);
+ if (*endptr != '\0') {
+ return EINVAL;
+ }
+ if (result < type->min.u32 || result > type->max.u32) {
+ return ERANGE;
+ }
+ *((int32_t*)type->target) = result;
+ *extra_args_consumed = 1;
+ return 0;
+}
+
+static inline int
+parse_int32(struct arg_type *type, int *extra_args_consumed, int argc, char *const argv[]) {
+ // Already verified name.
+
+ if (argc < 2) {
+ return EINVAL;
+ }
+ if (*argv[2] == '\0') {
+ return EINVAL;
+ }
+
+ char *endptr;
+ long int result = strtol(argv[2], &endptr, 0);
+ if (*endptr != '\0') {
+ return EINVAL;
+ }
+ if (result < type->min.i32 || result > type->max.i32) {
+ return ERANGE;
+ }
+ *((int32_t*)type->target) = result;
+ *extra_args_consumed = 1;
+ return 0;
+}
+
+static inline int
+parse_double(struct arg_type *type, int *extra_args_consumed, int argc, char *const argv[]) {
+ // Already verified name.
+
+ if (argc < 2) {
+ return EINVAL;
+ }
+ if (*argv[2] == '\0') {
+ return EINVAL;
+ }
+
+ char *endptr;
+ double result = strtod(argv[2], &endptr);
+ if (*endptr != '\0') {
+ return EINVAL;
+ }
+ if (result < type->min.d || result > type->max.d) {
+ return ERANGE;
+ }
+ *((double*)type->target) = result;
+ *extra_args_consumed = 1;
+ return 0;
+}
+
+// Common case (match_name).
+#define DECLARE_TYPE_DESCRIPTION(typename) \
+ struct type_description type_##typename = { \
+ .type_name = #typename, \
+ .matches = match_name, \
+ .parse = parse_##typename, \
+ .help = help_##typename \
+ }
+DECLARE_TYPE_DESCRIPTION(int32);
+DECLARE_TYPE_DESCRIPTION(uint32);
+DECLARE_TYPE_DESCRIPTION(int64);
+DECLARE_TYPE_DESCRIPTION(uint64);
+DECLARE_TYPE_DESCRIPTION(double);
+DECLARE_TYPE_DESCRIPTION(string);
+
+// Bools use their own match function so they are declared manually.
+struct type_description type_bool = {
+ .type_name = "bool",
+ .matches = match_bool,
+ .parse = parse_bool,
+ .help = help_bool
+};
+
+#define ARG_MATCHES(type, rest...) type->description->matches(type, rest)
+#define ARG_PARSE(type, rest...) type->description->parse(type, rest)
+#define ARG_HELP(type, rest...) type->description->help(type, rest)
+
+static inline void
+do_usage(const char *argv0, int n, struct arg_type types[/*n*/]) {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "\t%s [-h|--help]\n", argv0);
+ fprintf(stderr, "\t%s [OPTIONS]\n", argv0);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "OPTIONS are among:\n");
+ fprintf(stderr, "\t-q|--quiet\n");
+ fprintf(stderr, "\t-v|--verbose\n");
+ for (int i = 0; i < n; i++) {
+ struct arg_type *type = &types[i];
+ ARG_HELP(type, 35, 6);
+ }
+}
+
+static inline void parse_stress_test_args (int argc, char *const argv[], struct cli_args *args) {
+ struct cli_args default_args = *args;
+ const char *argv0=argv[0];
+
+#define MAKE_ARG(name_string, type, member, variable, suffix, min_val, max_val) { \
+ .name=(name_string), \
+ .description=&(type), \
+ .default_val={.member=default_args.variable}, \
+ .target=&(args->variable), \
+ .help_suffix=(suffix), \
+ .min={.member=min_val}, \
+ .max={.member=max_val}, \
+}
+#define MAKE_LOCAL_ARG(name_string, type, member, default, variable, suffix, min_val, max_val) { \
+ .name=(name_string), \
+ .description=&(type), \
+ .default_val={.member=default}, \
+ .target=&(variable), \
+ .help_suffix=(suffix), \
+ .min={.member=min_val}, \
+ .max={.member=max_val}, \
+}
+#define UINT32_ARG(name_string, variable, suffix) \
+ MAKE_ARG(name_string, type_uint32, u32, variable, suffix, 0, UINT32_MAX)
+#define UINT32_ARG_R(name_string, variable, suffix, min, max) \
+ MAKE_ARG(name_string, type_uint32, u32, variable, suffix, min, max)
+#define UINT64_ARG(name_string, variable, suffix) \
+ MAKE_ARG(name_string, type_uint64, u64, variable, suffix, 0, UINT64_MAX)
+#define INT32_ARG_NONNEG(name_string, variable, suffix) \
+ MAKE_ARG(name_string, type_int32, i32, variable, suffix, 0, INT32_MAX)
+#define INT32_ARG_R(name_string, variable, suffix, min, max) \
+ MAKE_ARG(name_string, type_int32, i32, variable, suffix, min, max)
+#define DOUBLE_ARG_R(name_string, variable, suffix, min, max) \
+ MAKE_ARG(name_string, type_double, d, variable, suffix, min, max)
+#define BOOL_ARG(name_string, variable) \
+ MAKE_ARG(name_string, type_bool, b, variable, "", false, false)
+#define STRING_ARG(name_string, variable) \
+ MAKE_ARG(name_string, type_string, s, variable, "", "", "")
+#define LOCAL_STRING_ARG(name_string, variable, default) \
+ MAKE_LOCAL_ARG(name_string, type_string, s, default, variable, "", "", "")
+
+ const char *perf_format_s = nullptr;
+ const char *compression_method_s = nullptr;
+ const char *print_engine_status_s = nullptr;
+ struct arg_type arg_types[] = {
+ INT32_ARG_NONNEG("--num_elements", num_elements, ""),
+ INT32_ARG_NONNEG("--num_DBs", num_DBs, ""),
+ INT32_ARG_NONNEG("--num_seconds", num_seconds, "s"),
+ INT32_ARG_NONNEG("--fanout", env_args.fanout, ""),
+ INT32_ARG_NONNEG("--node_size", env_args.node_size, " bytes"),
+ INT32_ARG_NONNEG("--basement_node_size", env_args.basement_node_size, " bytes"),
+ INT32_ARG_NONNEG("--rollback_node_size", env_args.rollback_node_size, " bytes"),
+ INT32_ARG_NONNEG("--checkpointing_period", env_args.checkpointing_period, "s"),
+ INT32_ARG_NONNEG("--cleaner_period", env_args.cleaner_period, "s"),
+ INT32_ARG_NONNEG("--cleaner_iterations", env_args.cleaner_iterations, ""),
+ INT32_ARG_NONNEG("--sync_period", env_args.sync_period, "ms"),
+ INT32_ARG_NONNEG("--update_broadcast_period", update_broadcast_period_ms, "ms"),
+ INT32_ARG_NONNEG("--num_ptquery_threads", num_ptquery_threads, " threads"),
+ INT32_ARG_NONNEG("--num_put_threads", num_put_threads, " threads"),
+ INT32_ARG_NONNEG("--num_update_threads", num_update_threads, " threads"),
+ INT32_ARG_NONNEG("--range_query_limit", range_query_limit, " rows"),
+
+ UINT32_ARG("--txn_size", txn_size, " rows"),
+ UINT32_ARG("--num_bucket_mutexes", env_args.num_bucket_mutexes, " mutexes"),
+
+ INT32_ARG_R("--join_timeout", join_timeout, "s", 1, INT32_MAX),
+ INT32_ARG_R("--performance_period", performance_period, "s", 1, INT32_MAX),
+
+ UINT64_ARG("--cachetable_size", env_args.cachetable_size, " bytes"),
+ UINT64_ARG("--lk_max_memory", env_args.lk_max_memory, " bytes"),
+
+ DOUBLE_ARG_R("--compressibility", compressibility, "", 0.0, 1.0),
+
+ //TODO: when outputting help.. skip min/max that is min/max of data range.
+ UINT32_ARG_R("--key_size", key_size, " bytes", min_key_size, UINT32_MAX),
+ UINT32_ARG_R("--val_size", val_size, " bytes", min_val_size, UINT32_MAX),
+
+ BOOL_ARG("serial_insert", serial_insert),
+ BOOL_ARG("interleave", interleave),
+ BOOL_ARG("crash_on_operation_failure", crash_on_operation_failure),
+ BOOL_ARG("single_txn", single_txn),
+ BOOL_ARG("warm_cache", warm_cache),
+ BOOL_ARG("print_performance", print_performance),
+ BOOL_ARG("print_thread_performance", print_thread_performance),
+ BOOL_ARG("print_iteration_performance", print_iteration_performance),
+ BOOL_ARG("only_create", only_create),
+ BOOL_ARG("only_stress", only_stress),
+ BOOL_ARG("test", do_test_and_crash),
+ BOOL_ARG("recover", do_recover),
+ BOOL_ARG("blackhole", blackhole),
+ BOOL_ARG("nolocktree", nolocktree),
+ BOOL_ARG("unique_checks", unique_checks),
+ BOOL_ARG("nolog", nolog),
+ BOOL_ARG("nocrashstatus", nocrashstatus),
+ BOOL_ARG("prelock_updates", prelock_updates),
+ BOOL_ARG("disperse_keys", disperse_keys),
+ BOOL_ARG("memcmp_keys", memcmp_keys),
+ BOOL_ARG("direct_io", direct_io),
+
+ STRING_ARG("--envdir", env_args.envdir),
+
+ LOCAL_STRING_ARG("--perf_format", perf_format_s, "human"),
+ LOCAL_STRING_ARG("--compression_method", compression_method_s, "quicklz"),
+ LOCAL_STRING_ARG("--print_engine_status", print_engine_status_s, nullptr),
+ //TODO(add --quiet, -v, -h)
+ };
+#undef UINT32_ARG
+#undef UINT32_ARG_R
+#undef UINT64_ARG
+#undef DOUBLE_ARG_R
+#undef BOOL_ARG
+#undef STRING_ARG
+#undef MAKE_ARG
+
+ int num_arg_types = sizeof(arg_types) / sizeof(arg_types[0]);
+
+ int resultcode = 0;
+ while (argc > 1) {
+ if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--verbose")) {
+ verbose++;
+ argv++;
+ argc--;
+ }
+ else if (!strcmp(argv[1], "-q") || !strcmp(argv[1], "--quiet")) {
+ verbose = 0;
+ argv++;
+ argc--;
+ }
+ else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
+ fprintf(stderr, "HELP INVOKED\n");
+ do_usage(argv0, num_arg_types, arg_types);
+ exit(0);
+ }
+ else {
+ bool found = false;
+ for (int i = 0; i < num_arg_types; i++) {
+ struct arg_type *type = &arg_types[i];
+ if (ARG_MATCHES(type, argv)) {
+ int extra_args_consumed;
+ resultcode = ARG_PARSE(type, &extra_args_consumed, argc, argv);
+ if (resultcode) {
+ fprintf(stderr, "ERROR PARSING [%s]\n", argv[1]);
+ do_usage(argv0, num_arg_types, arg_types);
+ exit(resultcode);
+ }
+ found = true;
+ argv += extra_args_consumed + 1;
+ argc -= extra_args_consumed + 1;
+ break;
+ }
+ }
+ if (!found) {
+ fprintf(stderr, "COULD NOT PARSE [%s]\n", argv[1]);
+ do_usage(argv0, num_arg_types, arg_types);
+ exit(EINVAL);
+ }
+ }
+ }
+ args->print_engine_status = print_engine_status_s;
+ if (compression_method_s != nullptr) {
+ if (strcmp(compression_method_s, "quicklz") == 0) {
+ args->compression_method = TOKU_QUICKLZ_METHOD;
+ } else if (strcmp(compression_method_s, "zlib") == 0) {
+ args->compression_method = TOKU_ZLIB_WITHOUT_CHECKSUM_METHOD;
+ } else if (strcmp(compression_method_s, "lzma") == 0) {
+ args->compression_method = TOKU_LZMA_METHOD;
+ } else if (strcmp(compression_method_s, "snappy") == 0) {
+ args->compression_method = TOKU_SNAPPY_METHOD;
+ } else if (strcmp(compression_method_s, "none") == 0) {
+ args->compression_method = TOKU_NO_COMPRESSION;
+ } else {
+ fprintf(stderr, "valid values for --compression_method are \"quicklz\", \"zlib\", \"lzma\", \"snappy\", and \"none\"\n");
+ do_usage(argv0, num_arg_types, arg_types);
+ exit(EINVAL);
+ }
+ }
+ if (perf_format_s != nullptr) {
+ if (!strcmp(perf_format_s, "human")) {
+ args->perf_output_format = HUMAN;
+ } else if (!strcmp(perf_format_s, "csv")) {
+ args->perf_output_format = CSV;
+ } else if (!strcmp(perf_format_s, "tsv")) {
+ args->perf_output_format = TSV;
+ } else {
+ fprintf(stderr, "valid values for --perf_format are \"human\", \"csv\", and \"tsv\"\n");
+ do_usage(argv0, num_arg_types, arg_types);
+ exit(EINVAL);
+ }
+ }
+ if (args->only_create && args->only_stress) {
+ fprintf(stderr, "used --only_stress and --only_create\n");
+ do_usage(argv0, num_arg_types, arg_types);
+ exit(EINVAL);
+ }
+}
+
+static void
+stress_table(DB_ENV *, DB **, struct cli_args *);
+
+static int
+stress_dbt_cmp_legacy(const DBT *a, const DBT *b) {
+ int x = *(int *) a->data;
+ int y = *(int *) b->data;
+ if (x < y) {
+ return -1;
+ } else if (x > y) {
+ return +1;
+ } else {
+ return 0;
+ }
+}
+
+static int
+stress_dbt_cmp(const DBT *a, const DBT *b) {
+ // Keys are only compared by their first 8 bytes,
+ // interpreted as a little endian 64 bit integers.
+ // The rest of the key is just padding.
+ uint64_t x = *(uint64_t *) a->data;
+ uint64_t y = *(uint64_t *) b->data;
+ if (x < y) {
+ return -1;
+ } else if (x > y) {
+ return +1;
+ } else {
+ return 0;
+ }
+}
+
+static int
+stress_cmp(DB *db, const DBT *a, const DBT *b) {
+ assert(db && a && b);
+ assert(a->size == b->size);
+
+ if (a->size == sizeof(int)) {
+ // Legacy comparison: keys must be >= 4 bytes
+ return stress_dbt_cmp_legacy(a, b);
+ } else {
+ // Modern comparison: keys must be >= 8 bytes
+ invariant(a->size >= sizeof(uint64_t));
+ return stress_dbt_cmp(a, b);
+ }
+}
+
+static void
+do_warm_cache(DB_ENV *env, DB **dbs, struct cli_args *args)
+{
+ struct scan_op_extra soe;
+ soe.fast = true;
+ soe.fwd = true;
+ soe.prefetch = true;
+ struct arg scan_arg;
+ arg_init(&scan_arg, dbs, env, args);
+ scan_arg.operation_extra = &soe;
+ scan_arg.operation = scan_op_no_check;
+ scan_arg.lock_type = STRESS_LOCK_NONE;
+ DB_TXN* txn = nullptr;
+ // don't take serializable read locks when scanning.
+ int r = env->txn_begin(env, 0, &txn, DB_TXN_SNAPSHOT); CKERR(r);
+ // make sure the scan doesn't terminate early
+ run_test = true;
+ // warm up each DB in parallel
+ scan_op_no_check_parallel(txn, &scan_arg, &soe, nullptr);
+ r = txn->commit(txn,0); CKERR(r);
+}
+
+static void
+UU() stress_recover(struct cli_args *args) {
+ DB_ENV* env = nullptr;
+ DB* dbs[args->num_DBs];
+ memset(dbs, 0, sizeof(dbs));
+ { int chk_r = open_tables(&env,
+ dbs,
+ args->num_DBs,
+ stress_cmp,
+ args); CKERR(chk_r); }
+
+ DB_TXN* txn = nullptr;
+ struct arg recover_args;
+ arg_init(&recover_args, dbs, env, args);
+ int r = env->txn_begin(env, 0, &txn, recover_args.txn_flags);
+ CKERR(r);
+ struct scan_op_extra soe = {
+ .fast = true,
+ .fwd = true,
+ .prefetch = false
+ };
+ // make sure the scan doesn't terminate early
+ run_test = true;
+ r = scan_op(txn, &recover_args, &soe, nullptr);
+ CKERR(r);
+ { int chk_r = txn->commit(txn,0); CKERR(chk_r); }
+ { int chk_r = close_tables(env, dbs, args->num_DBs); CKERR(chk_r); }
+}
+
+static void
+open_and_stress_tables(struct cli_args *args, bool fill_with_zeroes, int (*cmp)(DB *, const DBT *, const DBT *))
+{
+ if ((args->key_size < 8 && args->key_size != 4) ||
+ (args->val_size < 8 && args->val_size != 4)) {
+ fprintf(stderr, "The only valid key/val sizes are 4, 8, and > 8.\n");
+ return;
+ }
+
+ setlocale(LC_NUMERIC, "en_US.UTF-8");
+ DB_ENV* env = nullptr;
+ DB* dbs[args->num_DBs];
+ memset(dbs, 0, sizeof(dbs));
+ db_env_enable_engine_status(args->nocrashstatus ? false : true);
+ db_env_set_direct_io(args->direct_io ? true : false);
+ if (!args->only_stress) {
+ create_tables(
+ &env,
+ dbs,
+ args->num_DBs,
+ cmp,
+ args
+ );
+ { int chk_r = fill_tables(env, dbs, args, fill_with_zeroes); CKERR(chk_r); }
+ { int chk_r = close_tables(env, dbs, args->num_DBs); CKERR(chk_r); }
+ }
+ if (!args->only_create) {
+ { int chk_r = open_tables(&env,
+ dbs,
+ args->num_DBs,
+ cmp,
+ args); CKERR(chk_r); }
+ if (args->warm_cache) {
+ do_warm_cache(env, dbs, args);
+ }
+ stress_table(env, dbs, args);
+ { int chk_r = close_tables(env, dbs, args->num_DBs); CKERR(chk_r); }
+ }
+}
+
+static void
+UU() stress_test_main(struct cli_args *args) {
+ // Begin the test with fixed size values equal to zero.
+ // This is important for correctness testing.
+ open_and_stress_tables(args, true, stress_cmp);
+}
+
+static void
+UU() perf_test_main(struct cli_args *args) {
+ // Do not begin the test by creating a table of all zeroes.
+ // We want to control the row size and its compressibility.
+ open_and_stress_tables(args, false, stress_cmp);
+}
+
+static void
+UU() perf_test_main_with_cmp(struct cli_args *args, int (*cmp)(DB *, const DBT *, const DBT *)) {
+ // Do not begin the test by creating a table of all zeroes.
+ // We want to control the row size and its compressibility.
+ open_and_stress_tables(args, false, cmp);
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/time_create_db.cc b/storage/tokudb/PerconaFT/src/tests/time_create_db.cc
new file mode 100644
index 00000000000..f8f56e8516a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/time_create_db.cc
@@ -0,0 +1,122 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include <vector>
+#include <db.h>
+#include "toku_time.h"
+
+static void open_dbs(DB_ENV *env, int max_dbs) {
+ std::vector<DB *> dbs;
+
+ uint64_t t_start = toku_current_time_microsec();
+ // open db's
+ {
+ uint64_t t0 = toku_current_time_microsec();
+ for (int i = 1; i <= max_dbs; i++) {
+ int r;
+ DB *db = NULL;
+ r = db_create(&db, env, 0);
+ assert(r == 0);
+ char db_name[32];
+ sprintf(db_name, "db%d", i);
+ r = db->open(db, NULL, db_name, NULL, DB_BTREE, DB_CREATE, 0666);
+ assert(r == 0);
+ dbs.push_back(db);
+ if ((i % 100) == 0) {
+ uint64_t t = toku_current_time_microsec();
+ fprintf(stderr, "open %d %" PRIu64 "\n", i, t - t0);
+ t0 = t;
+ }
+ }
+ }
+ uint64_t t_end = toku_current_time_microsec();
+ fprintf(stderr, "%" PRIu64 "\n", t_end - t_start);
+
+ // close db's
+ {
+ uint64_t t0 = toku_current_time_microsec();
+ int i = 1;
+ for (std::vector<DB *>::iterator dbi = dbs.begin(); dbi != dbs.end(); dbi++, i++) {
+ DB *db = *dbi;
+ int r = db->close(db, 0);
+ assert(r == 0);
+ if ((i % 100) == 0) {
+ uint64_t t = toku_current_time_microsec();
+ printf("close %d %" PRIu64 "\n", i, t - t0);
+ t0 = t;
+ }
+ }
+ }
+}
+
+int test_main (int argc, char * const argv[]) {
+ int r;
+ int max_dbs = 1;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0) verbose--;
+ continue;
+ }
+ max_dbs = atoi(argv[i]);
+ continue;
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0);
+ assert(r == 0);
+ env->set_errfile(env, stderr);
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK+DB_INIT_MPOOL+DB_INIT_TXN+DB_INIT_LOG + DB_CREATE + DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ assert(r == 0);
+
+ open_dbs(env, max_dbs);
+
+ r = env->close(env, 0);
+ assert(r == 0);
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/transactional_fileops.cc b/storage/tokudb/PerconaFT/src/tests/transactional_fileops.cc
new file mode 100644
index 00000000000..c1e7c5b21c4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/transactional_fileops.cc
@@ -0,0 +1,459 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Purpose of this test is to verify correct behavior of transactional file
+ * operations. File operations to be tested (and expected results) are:
+ * - open
+ * - create (dictionary is created only if transaction is committed)
+ * - rename (dictionary is renamed only if transaction is committed)
+ * - delete (dictionary is deleted only if transaction is committed)
+ *
+ * The following subtests are here:
+ *
+ * test_fileops_1:
+ * Verify that operations appear effective within a transaction,
+ * but are truly effective only if the transaction is committed.
+ *
+ * test_fileops_2:
+ * Verify that attempting to open, remove or rename a dictionary that
+ * is marked for removal or renaming by another transaction in
+ * progress results in a DB_LOCK_NOTGRANTED error code.
+ *
+ * test_fileops_3:
+ * Verify that the correct error codes are returned when attempting
+ * miscellaneous operations that should fail.
+ *
+ *
+ * Future work (possible enhancements to this test, if desired):
+ * - verify correct behavior with "subdb" names (e.g. foo/bar)
+ * - beyond verifying that a dictionary exists, open it and read one entry, verify that the entry is correct
+ * (especially useful for renamed dictionary)
+ * - perform repeatedly in multiple threads
+ *
+ */
+
+
+#include "test.h"
+#include <db.h>
+
+static DB_ENV *env;
+static FILE *error_file = NULL;
+
+static void
+setup (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ if (verbose==0) {
+ char errfname[TOKU_PATH_MAX+1];
+ error_file = fopen(toku_path_join(errfname, 2, TOKU_TEST_FILENAME, "stderr"), "w"); assert(error_file);
+ }
+ else error_file = stderr;
+
+ r=db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, error_file ? error_file : stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+}
+
+
+
+static void
+test_shutdown(void) {
+ int r;
+ r=env->close(env, 0); CKERR(r);
+ if (verbose==0) {
+ fclose(error_file);
+ error_file = NULL;
+ }
+}
+
+
+// create dictionaries a.db, b.db, c.db
+static void
+create_abcd(void) {
+ int r;
+ DB_TXN * txn;
+ DB * db_a;
+ DB * db_b;
+ DB * db_c;
+ DB * db_d;
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db_create(&db_a, env, 0); CKERR(r);
+ r=db_create(&db_b, env, 0); CKERR(r);
+ r=db_create(&db_c, env, 0); CKERR(r);
+ r=db_create(&db_d, env, 0); CKERR(r);
+
+ r=db_a->open(db_a, txn, "a.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_b->open(db_b, txn, "b.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_c->open(db_c, txn, "c.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_d->open(db_d, txn, "d.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+
+ r=db_a->close(db_a, 0); CKERR(r);
+ r=db_b->close(db_b, 0); CKERR(r);
+ r=db_c->close(db_c, 0); CKERR(r);
+
+ r=txn->commit(txn, 0); CKERR(r);
+
+ r=db_d->close(db_d, 0); CKERR(r); //Should work whether close is before or after commit. Do one after.
+}
+
+
+
+// delete b
+// rename c to c2
+// create x
+static void
+perform_ops(DB_TXN * txn) {
+ int r;
+ DB * db_x;
+
+ r = env->dbremove(env, txn, "b.db", NULL, 0); CKERR(r);
+
+ r = env->dbrename(env, txn, "c.db", NULL, "c2.db", 0); CKERR(r);
+
+ r=db_create(&db_x, env, 0); CKERR(r);
+ r=db_x->open(db_x, txn, "x.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_x->close(db_x, 0); CKERR(r); // abort requires db be closed first
+}
+
+
+// verify that:
+// dictionaries a.db, b.db, c.db, d.db exist
+// dictionaries x.db and c2.db do not exist
+static void
+verify_abcd(void) {
+ int r;
+ DB_TXN * txn;
+ DB * db_a;
+ DB * db_b;
+ DB * db_c;
+ DB * db_d;
+ DB * db_x;
+ DB * db_c2;
+
+ r=env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ r=db_create(&db_a, env, 0); CKERR(r);
+ r=db_create(&db_b, env, 0); CKERR(r);
+ r=db_create(&db_c, env, 0); CKERR(r);
+ r=db_create(&db_d, env, 0); CKERR(r);
+ r=db_create(&db_x, env, 0); CKERR(r);
+ r=db_create(&db_c2, env, 0); CKERR(r);
+
+ // should exist:
+ r=db_a->open(db_a, txn, "a.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_b->open(db_b, txn, "b.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_c->open(db_c, txn, "c.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_d->open(db_d, txn, "d.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+
+ // should not exist:
+ r=db_x->open(db_x, txn, "x.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR2(r, ENOENT);
+ r=db_c2->open(db_c2, txn, "c2.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR2(r, ENOENT);
+
+ r=db_a->close(db_a, 0); CKERR(r);
+ r=db_b->close(db_b, 0); CKERR(r);
+ r=db_c->close(db_c, 0); CKERR(r);
+ r=db_d->close(db_d, 0); CKERR(r);
+ r=db_x->close(db_x, 0); CKERR(r);
+ r=db_c2->close(db_c2, 0); CKERR(r);
+
+ r=txn->commit(txn, 0); CKERR(r);
+}
+
+
+// verify that:
+// dictionary a.db exists
+// dictionaries b.db, c.db do not exist
+// dictionary c2.db exists
+// dictionary d.db exists
+// dictionary x.db exists
+static void
+verify_ac2dx(DB_TXN * parent_txn) {
+ int r;
+ DB_TXN * txn;
+ DB * db_a;
+ DB * db_b;
+ DB * db_c;
+ DB * db_d;
+ DB * db_x;
+ DB * db_c2;
+
+ r=env->txn_begin(env, parent_txn, &txn, 0); CKERR(r);
+ r=db_create(&db_a, env, 0); CKERR(r);
+ r=db_create(&db_b, env, 0); CKERR(r);
+ r=db_create(&db_c, env, 0); CKERR(r);
+ r=db_create(&db_d, env, 0); CKERR(r);
+ r=db_create(&db_x, env, 0); CKERR(r);
+ r=db_create(&db_c2, env, 0); CKERR(r);
+
+ // should exist:
+ r=db_a->open(db_a, txn, "a.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_c2->open(db_c2, txn, "c2.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_d->open(db_d, txn, "d.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_x->open(db_x, txn, "x.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+
+ // should not exist:
+ r=db_b->open(db_b, txn, "b.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR2(r, ENOENT);
+ r=db_c->open(db_c, txn, "c.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR2(r, ENOENT);
+
+ r=db_a->close(db_a, 0); CKERR(r);
+ r=db_b->close(db_b, 0); CKERR(r);
+ r=db_c->close(db_c, 0); CKERR(r);
+ r=db_d->close(db_d, 0); CKERR(r);
+ r=db_x->close(db_x, 0); CKERR(r);
+ r=db_c2->close(db_c2, 0); CKERR(r);
+
+ r=txn->commit(txn, 0); CKERR(r);
+}
+
+
+static void
+test_fileops_1(void) {
+ int r;
+ DB_TXN *txn;
+
+ create_abcd();
+ verify_abcd();
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ perform_ops(txn);
+ verify_ac2dx(txn); // verify that operations appear effective within this txn
+ r=txn->abort(txn); CKERR(r);
+
+ // verify that aborted transaction changed nothing
+ verify_abcd();
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ perform_ops(txn);
+ verify_ac2dx(txn); // verify that operations appear effective within this txn
+ r=txn->commit(txn, 0); CKERR(r);
+
+ // verify that committed transaction actually changed db
+ verify_ac2dx(NULL);
+}
+
+
+
+static void
+verify_locked_open(const char * name) {
+ int r;
+ DB_TXN * txn;
+ DB * db;
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, txn, name, 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r=db->close(db, 0); CKERR(r); // always safe to close
+ r=txn->abort(txn); CKERR(r);
+}
+
+static void
+verify_locked_remove(const char * name) {
+ int r;
+ DB_TXN * txn;
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = env->dbremove(env, txn, name, NULL, 0);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r=txn->abort(txn); CKERR(r);
+}
+
+static void
+verify_locked_rename(const char * oldname, const char * newname) {
+ int r;
+ DB_TXN * txn;
+
+ r=env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = env->dbrename(env, txn, oldname, NULL, newname, 0);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r=txn->abort(txn); CKERR(r);
+}
+
+
+// Purpose of test_fileops_2() is to verify correct operation of
+// directory range locks. It should not be possible to open or
+// rename or remove a dictionary that is marked for removal or
+// rename by another open transaction.
+static void
+test_fileops_2(void) {
+ int r;
+ DB_TXN * txn_a;
+
+ verify_ac2dx(NULL); // should still exist
+
+ // begin txn_a
+ // remove a
+ // create e
+ // rename x->x2
+ // rename c2->c3
+ // open x2, c3, should succeed
+ // close x2, c3
+ {
+ DB * db_e;
+ DB * db_c3;
+ DB * db_x2;
+
+ r=env->txn_begin(env, 0, &txn_a, 0); CKERR(r);
+ r=db_create(&db_e, env, 0); CKERR(r);
+ r=db_create(&db_x2, env, 0); CKERR(r);
+ r=db_create(&db_c3, env, 0); CKERR(r);
+
+ r = env->dbremove(env, txn_a, "a.db", NULL, 0); CKERR(r);
+ r=db_e->open(db_e, txn_a, "e.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r = env->dbrename(env, txn_a, "x.db", NULL, "x2.db", 0); CKERR(r);
+ r = env->dbrename(env, txn_a, "c2.db", NULL, "c3.db", 0); CKERR(r);
+
+ r=db_x2->open(db_x2, txn_a, "x2.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db_c3->open(db_c3, txn_a, "c3.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+
+ r=db_e->close(db_e, 0); CKERR(r); // abort requires db be closed first
+ r=db_x2->close(db_x2, 0); CKERR(r); // abort requires db be closed first
+ r=db_c3->close(db_c3, 0); CKERR(r); // abort requires db be closed first
+
+ }
+
+ // within another transaction:
+ // open a, should fail DB_LOCK_NOTGRANTED
+ // open e, should fail DB_LOCK_NOTGRANTED
+ // open x, should fail DB_LOCK_NOTGRANTED
+ // open x2, should fail DB_LOCK_NOTGRANTED
+ // open c2, should fail DB_LOCK_NOTGRANTED
+ // open c3, should fail DB_LOCK_NOTGRANTED
+ // remove a, e, x, x2, c2, c3 DB_LOCK_NOTGRANTED
+ // rename a, e, x, x2, c2, c3 DB_LOCK_NOTGRANTED
+
+ verify_locked_open("a.db");
+ verify_locked_open("e.db");
+ verify_locked_open("x.db");
+ verify_locked_open("x2.db");
+ verify_locked_open("c2.db");
+ verify_locked_open("c3.db");
+
+ verify_locked_remove("a.db");
+ verify_locked_remove("e.db");
+ verify_locked_remove("x.db");
+ verify_locked_remove("x2.db");
+ verify_locked_remove("c2.db");
+ verify_locked_remove("c3.db");
+
+ verify_locked_rename("a.db", "z.db");
+ verify_locked_rename("e.db", "z.db");
+ verify_locked_rename("x.db", "z.db");
+ verify_locked_rename("x2.db", "z.db");
+ verify_locked_rename("c2.db", "z.db");
+ verify_locked_rename("c3.db", "z.db");
+
+ verify_locked_rename("d.db", "a.db");
+ verify_locked_rename("d.db", "e.db");
+ verify_locked_rename("d.db", "x.db");
+ verify_locked_rename("d.db", "x2.db");
+ verify_locked_rename("d.db", "c2.db");
+ verify_locked_rename("d.db", "c3.db");
+
+
+ r=txn_a->abort(txn_a); CKERR(r);
+
+}
+
+
+static void
+test_fileops_3(void) {
+ // verify cannot remove an open db
+
+ int r;
+ DB_TXN * txn_a;
+ DB_TXN * txn_b;
+ DB * db_d;
+
+ r=env->txn_begin(env, 0, &txn_a, 0); CKERR(r);
+ r=db_create(&db_d, env, 0); CKERR(r);
+ r=db_d->open(db_d, txn_a, "d.db", 0, DB_BTREE, 0, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+
+ // Verify correct error return codes when trying to
+ // remove or rename an open dictionary
+ r=env->txn_begin(env, 0, &txn_b, 0); CKERR(r);
+ r = env->dbremove(env, txn_b, "d.db", NULL, 0);
+ CKERR2(r, EINVAL);
+ r = env->dbrename(env, txn_b, "d.db", NULL, "z.db", 0);
+ CKERR2(r, EINVAL);
+ r = env->dbrename(env, txn_b, "a.db", NULL, "d.db", 0);
+ CKERR2(r, EINVAL);
+ r=db_d->close(db_d, 0); CKERR(r);
+ r=txn_b->abort(txn_b); CKERR(r);
+
+
+ // verify correct error return codes when trying to
+ // remove or rename a non-existent dictionary
+ r = env->dbremove(env, txn_a, "nonexistent.db", NULL, 0);
+ CKERR2(r, ENOENT);
+ r = env->dbrename(env, txn_a, "nonexistent.db", NULL, "z.db", 0);
+ CKERR2(r, ENOENT);
+
+ // verify correct error return code when trying to
+ // rename a dictionary to a name that already exists
+ r = env->dbrename(env, txn_a, "a.db", NULL, "d.db", 0);
+ CKERR2(r, EEXIST);
+
+ r=txn_a->abort(txn_a); CKERR(r);
+}
+
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ if (verbose >= 2) {
+ printf("Immediately after setup:\n");
+ print_engine_status(env);
+ }
+ test_fileops_1();
+ if (verbose >= 2) {
+ printf("After test_1:\n");
+ print_engine_status(env);
+ }
+ test_fileops_2();
+ test_fileops_3();
+ if (verbose >= 2) {
+ printf("After test_2 and test_3:\n");
+ print_engine_status(env);
+ }
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/update-multiple-data-diagonal.cc b/storage/tokudb/PerconaFT/src/tests/update-multiple-data-diagonal.cc
new file mode 100644
index 00000000000..632942df2d3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/update-multiple-data-diagonal.cc
@@ -0,0 +1,343 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that update_multiple where we change the data in row[i] col[j] from x to x+1
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(2*(i + dbnum));
+}
+
+static int
+get_new_key(int i, int dbnum) {
+ return htonl(2*(i + dbnum) + 1);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static void
+get_new_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if ((i % ndbs) == dbnum)
+ v[dbnum] = get_new_key(i, dbnum);
+ else
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = NULL;
+ if (dest_val_arrays) {
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ dest_val = &dest_val_arrays->dbts[0];
+ }
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_key = (int *) src_key->data;
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = dbnum == 0 ? &pri_key[dbnum] : &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, dbnum == 0 ? &pri_key[dbnum] : &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else
+ dest_val->size = 0;
+ break;
+ case DB_DBT_REALLOC:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = toku_realloc(dest_val->data, dest_val->size);
+ memcpy(dest_val->data, src_val->data, dest_val->size);
+ } else
+ dest_val->size = 0;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_key_arrays, NULL, src_key, src_data);
+}
+
+#if 0
+static void
+verify_locked(DB_ENV *env, DB *db, int k) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn->abort(txn); assert_zero(r);
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+#endif
+
+static void
+verify_seq(DB_ENV *env, DB *db, int dbnum, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ int expectk;
+ if (dbnum == 0 || (i % ndbs) != dbnum)
+ expectk = get_key(i, dbnum);
+ else
+ expectk = get_new_key(i, dbnum);
+
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == expectk);
+
+ if (dbnum == 0) {
+ assert(val.size == ndbs * sizeof (int));
+ int v[ndbs]; get_new_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ } else
+ assert(val.size == 0);
+ }
+ assert(i == nrows); // if (i != nrows) printf("%s:%d %d %d\n", __FUNCTION__, __LINE__, i, nrows); // assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+update_diagonal(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ assert(ndbs > 0);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ for (int i = 0; i < nrows; i++) {
+
+ // update the data i % ndbs col from x to x+1
+
+ int k = get_key(i, 0);
+ DBT old_key; dbt_init(&old_key, &k, sizeof k);
+ DBT new_key = old_key;
+
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT old_data; dbt_init(&old_data, &v[0], sizeof v);
+
+ int newv[ndbs]; get_new_data(newv, i, ndbs);
+ DBT new_data; dbt_init(&new_data, &newv[0], sizeof newv);
+
+ int ndbts = 2 * ndbs;
+ DBT keys[ndbts]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbts]; memset(vals, 0, sizeof vals);
+ uint32_t flags_array[ndbs]; memset(flags_array, 0, sizeof(flags_array));
+
+ r = env_update_multiple_test_no_array(env, ndbs > 0 ? db[0] : NULL, txn, &old_key, &old_data, &new_key, &new_data, ndbs, db, flags_array, ndbts, keys, ndbts, vals);
+ assert_zero(r);
+ }
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, dbnum);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ update_diagonal(env, db, ndbs, nrows);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_seq(env, db[dbnum], dbnum, ndbs, nrows);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 2;
+ int nrows = 2;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/update-multiple-key0.cc b/storage/tokudb/PerconaFT/src/tests/update-multiple-key0.cc
new file mode 100644
index 00000000000..b9ae33b7eb9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/update-multiple-key0.cc
@@ -0,0 +1,327 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that update_multiple where we only change key0
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = NULL;
+ if (dest_val_arrays) {
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ dest_val = &dest_val_arrays->dbts[0];
+ }
+
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_key = (int *) src_key->data;
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = dbnum == 0 ? &pri_key[dbnum] : &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, dbnum == 0 ? &pri_key[dbnum] : &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else
+ dest_val->size = 0;
+ break;
+ case DB_DBT_REALLOC:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = toku_realloc(dest_val->data, dest_val->size);
+ memcpy(dest_val->data, src_val->data, dest_val->size);
+ } else
+ dest_val->size = 0;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_key_arrays, NULL, src_key, src_data);
+}
+
+static void
+verify_locked(DB_ENV *env, DB *db, int k) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn->abort(txn); assert_zero(r);
+}
+
+#if 0
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+#endif
+
+static void
+verify_seq(DB_ENV *env, DB *db, int dbnum, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ int expectk = dbnum == 0 ? get_key(i + nrows, dbnum) : get_key(i, dbnum);
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == expectk);
+
+ if (dbnum == 0) {
+ assert(val.size == ndbs * sizeof (int));
+ int v[ndbs]; get_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ } else
+ assert(val.size == 0);
+ }
+ assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+update_key0(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ assert(ndbs > 0);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ for (int i = 0; i < nrows; i++) {
+
+ // update where new key0 = old key0 + nrows
+
+ int k = get_key(i, 0);
+ DBT old_key; dbt_init(&old_key, &k, sizeof k);
+ int newk = get_key(i + nrows, 0);
+ DBT new_key; dbt_init(&new_key, &newk, sizeof newk);
+
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT old_data; dbt_init(&old_data, &v[0], sizeof v);
+ DBT new_data = old_data;
+
+ int ndbts = 2 * ndbs;
+ DBT keys[ndbts]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbts]; memset(vals, 0, sizeof vals);
+ uint32_t flags_array[ndbs]; memset(flags_array, 0, sizeof(flags_array));
+
+ r = env_update_multiple_test_no_array(env, ndbs > 0 ? db[0] : NULL, txn, &old_key, &old_data, &new_key, &new_data, ndbs, db, flags_array, ndbts, keys, ndbts, vals);
+ assert_zero(r);
+
+ verify_locked(env, db[0], k);
+ verify_locked(env, db[0], newk);
+ }
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, dbnum);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ update_key0(env, db, ndbs, nrows);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_seq(env, db[dbnum], dbnum, ndbs, nrows);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 2;
+ int nrows = 2;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/update-multiple-nochange.cc b/storage/tokudb/PerconaFT/src/tests/update-multiple-nochange.cc
new file mode 100644
index 00000000000..5bfac519b61
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/update-multiple-nochange.cc
@@ -0,0 +1,319 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that update_multiple where new row = old row
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(i + dbnum);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = NULL;
+ if (dest_val_arrays) {
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ dest_val = &dest_val_arrays->dbts[0];
+ }
+
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else
+ dest_val->size = 0;
+ break;
+ case DB_DBT_REALLOC:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = toku_realloc(dest_val->data, dest_val->size);
+ memcpy(dest_val->data, src_val->data, dest_val->size);
+ } else
+ dest_val->size = 0;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_key_arrays, NULL, src_key, src_data);
+}
+
+#if 0
+static void
+verify_locked(DB_ENV *env, DB *db, int k) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn->abort(txn); assert_zero(r);
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+#endif
+
+static void
+verify_seq(DB_ENV *env, DB *db, int dbnum, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == get_key(i, dbnum));
+
+ if (dbnum == 0) {
+ assert(val.size == ndbs * sizeof (int));
+ int v[ndbs]; get_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ } else
+ assert(val.size == 0);
+ }
+ assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ for (int i = 0; i < nrows; i++) {
+
+ // update where new row = old row
+
+ int k = get_key(i, 0);
+ DBT old_key; dbt_init(&old_key, &k, sizeof k);
+ DBT new_key = old_key;
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT old_data; dbt_init(&old_data, &v[0], sizeof v);
+ DBT new_data = old_data;
+
+ int ndbts = 2 * ndbs;
+ DBT keys[ndbts]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbts]; memset(vals, 0, sizeof vals);
+ uint32_t flags_array[ndbs]; memset(flags_array, 0, sizeof(flags_array));
+
+ r = env_update_multiple_test_no_array(env, ndbs > 0 ? db[0] : NULL, txn, &old_key, &old_data, &new_key, &new_data, ndbs, db, flags_array, ndbts, keys, ndbts, vals);
+ assert_zero(r);
+ }
+ r = txn->commit(txn, 0); assert_zero(r);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_seq(env, db[dbnum], dbnum, ndbs, nrows);
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, dbnum);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ verify(env, db, ndbs, nrows);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 2;
+ int nrows = 2;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/update-multiple-with-indexer-array.cc b/storage/tokudb/PerconaFT/src/tests/update-multiple-with-indexer-array.cc
new file mode 100644
index 00000000000..c79bca04d5e
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/update-multiple-with-indexer-array.cc
@@ -0,0 +1,459 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that update_multiple where we change the data in row[i] col[j] from x to x+1
+
+static const int MAX_KEYS = 3;
+
+static int
+array_size(int ndbs) {
+ return +
+ 1 + // 0 for old 1 for new
+ 1 + // ndbs
+ 2 * MAX_KEYS * (ndbs-1);
+}
+static int
+get_num_new_keys(int i, int dbnum) {
+ if (dbnum == 0) return 1;
+ if (i & (1<<4)) {
+ dbnum++; // Shift every once in a while.
+ }
+ return (i + dbnum) % MAX_KEYS; // 0, 1, or 2
+}
+
+static int
+get_old_num_keys(int i, int dbnum) {
+ if (dbnum == 0) return 1;
+ return (i + dbnum) % MAX_KEYS; // 0, 1, or 2
+}
+
+static int
+get_total_secondary_rows(int num_primary) {
+ assert(num_primary % MAX_KEYS == 0);
+ return num_primary / MAX_KEYS * (0 + 1 + 2);
+}
+
+static int
+get_old_key(int i, int dbnum, int which) {
+ assert(i < INT16_MAX / 2);
+ assert(which >= 0);
+ assert(which < 4);
+ assert(dbnum < 16);
+ if (dbnum == 0) {
+ assert(which == 0);
+ return htonl(2*i);
+ }
+ if (which >= get_old_num_keys(i, dbnum)) {
+ return htonl(-1);
+ }
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1));
+}
+
+static int
+get_new_key(int i, int dbnum, int which) {
+ assert(which >= 0);
+ assert(which < 4);
+ assert(dbnum < 16);
+
+ if (dbnum == 0) {
+ assert(which == 0);
+ return htonl(2*i);
+ }
+ if (which >= get_num_new_keys(i, dbnum)) {
+ return htonl(-1);
+ }
+ if ((i+dbnum+which) & (1<<5)) {
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1)); // no change from original
+ }
+ return htonl(((2*i+0) << 16) + (dbnum<<8) + (which<<1) + 1);
+}
+
+static void
+fill_data_2_and_later(int *v, int i, int ndbs) {
+ int index = 2;
+ for (int dbnum = 1; dbnum < ndbs; dbnum++) {
+ for (int which = 0; which < MAX_KEYS; ++which) {
+ v[index++] = get_old_key(i, dbnum, which);
+ }
+ }
+ for (int dbnum = 1; dbnum < ndbs; dbnum++) {
+ for (int which = 0; which < MAX_KEYS; ++which) {
+ v[index++] = get_new_key(i, dbnum, which);
+ }
+ }
+}
+
+
+static void
+fill_old_data(int *v, int i, int ndbs) {
+ v[0] = 0;
+ v[1] = ndbs;
+ fill_data_2_and_later(v, i, ndbs);
+}
+
+static void
+fill_new_data(int *v, int i, int ndbs) {
+ v[0] = 1;
+ v[1] = ndbs;
+ fill_data_2_and_later(v, i, ndbs);
+}
+
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ (void)src_val;
+ assert(src_db != dest_db);
+ assert(src_db);
+ int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum > 0);
+
+ int pri_key = *(int *) src_key->data;
+ int* pri_val = (int*) src_val->data;
+
+ bool is_new = pri_val[0] == 1;
+ int i = (ntohl(pri_key)) / 2;
+
+ int num_keys = is_new ? get_num_new_keys(i, dbnum) : get_old_num_keys(i, dbnum);
+
+ toku_dbt_array_resize(dest_key_arrays, num_keys);
+
+ if (dest_val_arrays) {
+ toku_dbt_array_resize(dest_val_arrays, num_keys);
+ }
+
+ int ndbs = pri_val[1];
+ int index = 2 + (dbnum-1)*MAX_KEYS;
+ if (is_new) {
+ index += MAX_KEYS*(ndbs-1);
+ }
+
+ assert(src_val->size % sizeof(int) == 0);
+ assert((int)src_val->size / 4 >= index + num_keys);
+
+
+ for (int which = 0; which < num_keys; which++) {
+ DBT *dest_key = &dest_key_arrays->dbts[which];
+ DBT *dest_val = NULL;
+
+ assert(dest_key->flags == DB_DBT_REALLOC);
+ if (dest_key->ulen < sizeof(int)) {
+ dest_key->data = toku_xrealloc(dest_key->data, sizeof(int));
+ dest_key->ulen = sizeof(int);
+ }
+ dest_key->size = sizeof(int);
+ if (dest_val_arrays) {
+ dest_val = &dest_val_arrays->dbts[which];
+ assert(dest_val->flags == DB_DBT_REALLOC);
+ dest_val->size = 0;
+ }
+ int new_key = is_new ? get_new_key(i, dbnum, which) : get_old_key(i, dbnum, which);
+ assert(new_key == pri_val[index + which]);
+ *(int*)dest_key->data = new_key;
+ }
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_key_arrays, NULL, src_key, src_data);
+}
+
+static void
+do_updates(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ assert(ndbs > 0);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ int narrays = 2 * ndbs;
+ DBT_ARRAY keys[narrays];
+ DBT_ARRAY vals[narrays];
+ for (int i = 0; i < narrays; i++) {
+ toku_dbt_array_init(&keys[i], 1);
+ toku_dbt_array_init(&vals[i], 1);
+ }
+
+ for (int i = 0; i < nrows; i++) {
+
+ // update the data i % ndbs col from x to x+1
+
+ int old_k = get_old_key(i, 0, 0);
+ DBT old_key; dbt_init(&old_key, &old_k, sizeof old_k);
+ int new_k = get_new_key(i, 0, 0);
+ DBT new_key; dbt_init(&new_key, &new_k, sizeof new_k);
+
+ int v[array_size(ndbs)]; fill_old_data(v, i, ndbs);
+ DBT old_data; dbt_init(&old_data, &v[0], sizeof v);
+
+ int newv[array_size(ndbs)]; fill_new_data(newv, i, ndbs);
+ DBT new_data; dbt_init(&new_data, &newv[0], sizeof newv);
+
+ uint32_t flags_array[ndbs]; memset(flags_array, 0, sizeof(flags_array));
+
+ r = env->update_multiple(env, db[0], txn, &old_key, &old_data, &new_key, &new_data, ndbs, db, flags_array, narrays, keys, narrays, vals);
+ assert_zero(r);
+ }
+ for (int i = 0; i < narrays; i++) {
+ toku_dbt_array_destroy(&keys[i]);
+ toku_dbt_array_destroy(&vals[i]);
+ }
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_old_key(i, 0, 0);
+ int v[array_size(ndbs)];
+ fill_old_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ for (int which = 0; which < MAX_KEYS; which++) {
+ int k = get_old_key(i, dbnum, which);
+ if (k >= 0) {
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+ }
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_pri_seq(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ const int dbnum = 0;
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ int expectk = get_new_key(i, dbnum, 0);
+
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == expectk);
+
+ int num_keys = array_size(ndbs);
+ assert(val.size == num_keys*sizeof(int));
+ int v[num_keys]; fill_new_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ }
+ assert(i == nrows); // if (i != nrows) printf("%s:%d %d %d\n", __FUNCTION__, __LINE__, i, nrows); // assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+verify_sec_seq(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ assert(dbnum > 0);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ int rows_found = 0;
+
+ for (i = 0; ; i++) {
+ int num_keys = get_num_new_keys(i, dbnum);
+ for (int which = 0; which < num_keys; ++which) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0) {
+ CKERR2(r, DB_NOTFOUND);
+ goto done;
+ }
+ rows_found++;
+ int k;
+ int expectk = get_new_key(i, dbnum, which);
+
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ int got_i = (ntohl(k) >> 16) / 2;
+ if (got_i < i) {
+ // Will fail. Too many old i's
+ assert(k == expectk);
+ } else if (got_i > i) {
+ // Will fail. Too few in previous i.
+ assert(k == expectk);
+ }
+
+ if (k != expectk && which < get_old_num_keys(i, dbnum) && k == get_old_key(i, dbnum, which)) {
+ // Will fail, never got updated.
+ assert(k == expectk);
+ }
+ assert(k == expectk);
+ assert(val.size == 0);
+ }
+ }
+done:
+ assert(rows_found == get_total_secondary_rows(nrows));
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ populate_primary(env, db[0], ndbs, nrows);
+ for (int dbnum = 1; dbnum < ndbs-1; dbnum++) {
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ DB_TXN *indexer_txn = NULL;
+ r = env->txn_begin(env, NULL, &indexer_txn, 0); assert_zero(r);
+
+ DB_INDEXER *indexer = NULL;
+ uint32_t db_flags = 0;
+ assert(ndbs > 2);
+ r = env->create_indexer(env, indexer_txn, &indexer, db[0], 1, &db[ndbs-1], &db_flags, 0); assert_zero(r);
+
+ do_updates(env, db, ndbs, nrows);
+
+ r = indexer->build(indexer); assert_zero(r);
+ r = indexer->close(indexer); assert_zero(r);
+
+ r = indexer_txn->commit(indexer_txn, 0); assert_zero(r);
+
+ verify_pri_seq(env, db[0], ndbs, nrows);
+ for (int dbnum = 1; dbnum < ndbs; dbnum++)
+ verify_sec_seq(env, db[dbnum], dbnum, nrows);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 10;
+ int nrows = MAX_KEYS*(1<<5)*4;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+ while (nrows % (MAX_KEYS*(1<<5)) != 0) {
+ nrows++;
+ }
+ //Need at least one to update, and one to index
+ while (ndbs < 3) {
+ ndbs++;
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/update-multiple-with-indexer.cc b/storage/tokudb/PerconaFT/src/tests/update-multiple-with-indexer.cc
new file mode 100644
index 00000000000..b1a95a1ef25
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/update-multiple-with-indexer.cc
@@ -0,0 +1,358 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// verify that update_multiple where we change the data in row[i] col[j] from x to x+1
+
+static int
+get_key(int i, int dbnum) {
+ return htonl(2*(i + dbnum));
+}
+
+static int
+get_new_key(int i, int dbnum) {
+ return htonl(2*(i + dbnum) + 1);
+}
+
+static void
+get_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static void
+get_new_data(int *v, int i, int ndbs) {
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ if ((i % ndbs) == dbnum)
+ v[dbnum] = get_new_key(i, dbnum);
+ else
+ v[dbnum] = get_key(i, dbnum);
+ }
+}
+
+static int
+put_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, DBT_ARRAY *dest_val_arrays, const DBT *src_key, const DBT *src_val) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = NULL;
+ if (dest_val_arrays) {
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ dest_val = &dest_val_arrays->dbts[0];
+ }
+
+ (void) dest_db; (void) src_db; (void) dest_key; (void) dest_val; (void) src_key; (void) src_val;
+
+ unsigned int dbnum;
+ assert(dest_db->descriptor->dbt.size == sizeof dbnum);
+ memcpy(&dbnum, dest_db->descriptor->dbt.data, sizeof dbnum);
+ assert(dbnum < src_val->size / sizeof (int));
+
+ int *pri_key = (int *) src_key->data;
+ int *pri_data = (int *) src_val->data;
+
+ switch (dest_key->flags) {
+ case 0:
+ dest_key->size = sizeof (int);
+ dest_key->data = dbnum == 0 ? &pri_key[dbnum] : &pri_data[dbnum];
+ break;
+ case DB_DBT_REALLOC:
+ dest_key->size = sizeof (int);
+ dest_key->data = toku_realloc(dest_key->data, dest_key->size);
+ memcpy(dest_key->data, dbnum == 0 ? &pri_key[dbnum] : &pri_data[dbnum], dest_key->size);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (dest_val) {
+ switch (dest_val->flags) {
+ case 0:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = src_val->data;
+ } else
+ dest_val->size = 0;
+ break;
+ case DB_DBT_REALLOC:
+ if (dbnum == 0) {
+ dest_val->size = src_val->size;
+ dest_val->data = toku_realloc(dest_val->data, dest_val->size);
+ memcpy(dest_val->data, src_val->data, dest_val->size);
+ } else
+ dest_val->size = 0;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+del_callback(DB *dest_db, DB *src_db, DBT_ARRAY *dest_key_arrays, const DBT *src_key, const DBT *src_data) {
+ return put_callback(dest_db, src_db, dest_key_arrays, NULL, src_key, src_data);
+}
+
+#if 0
+static void
+verify_locked(DB_ENV *env, DB *db, int k) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ r = db->del(db, txn, &key, DB_DELETE_ANY); assert(r == DB_LOCK_NOTGRANTED);
+ r = txn->abort(txn); assert_zero(r);
+}
+
+static void
+verify_empty(DB_ENV *env, DB *db) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ }
+ assert_zero(i);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+#endif
+
+static void
+verify_seq(DB_ENV *env, DB *db, int dbnum, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn, &cursor, 0); assert_zero(r);
+ int i;
+ for (i = 0; ; i++) {
+ DBT key; memset(&key, 0, sizeof key);
+ DBT val; memset(&val, 0, sizeof val);
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k;
+ int expectk;
+ if (dbnum == 0 || (i % ndbs) != dbnum)
+ expectk = get_key(i, dbnum);
+ else
+ expectk = get_new_key(i, dbnum);
+
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == expectk);
+
+ if (dbnum == 0) {
+ assert(val.size == ndbs * sizeof (int));
+ int v[ndbs]; get_new_data(v, i, ndbs);
+ assert(memcmp(val.data, v, val.size) == 0);
+ } else
+ assert(val.size == 0);
+ }
+ assert(i == nrows); // if (i != nrows) printf("%s:%d %d %d\n", __FUNCTION__, __LINE__, i, nrows); // assert(i == nrows);
+ r = cursor->c_close(cursor); assert_zero(r);
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+update_diagonal(DB_ENV *env, DB *db[], int ndbs, int nrows) {
+ assert(ndbs > 0);
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+ for (int i = 0; i < nrows; i++) {
+
+ // update the data i % ndbs col from x to x+1
+
+ int k = get_key(i, 0);
+ DBT old_key; dbt_init(&old_key, &k, sizeof k);
+ DBT new_key = old_key;
+
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT old_data; dbt_init(&old_data, &v[0], sizeof v);
+
+ int newv[ndbs]; get_new_data(newv, i, ndbs);
+ DBT new_data; dbt_init(&new_data, &newv[0], sizeof newv);
+
+ int ndbts = 2 * ndbs;
+ DBT keys[ndbts]; memset(keys, 0, sizeof keys);
+ DBT vals[ndbts]; memset(vals, 0, sizeof vals);
+ uint32_t flags_array[ndbs]; memset(flags_array, 0, sizeof(flags_array));
+
+ r = env_update_multiple_test_no_array(env, ndbs > 0 ? db[0] : NULL, txn, &old_key, &old_data, &new_key, &new_data, ndbs, db, flags_array, ndbts, keys, ndbts, vals);
+ assert_zero(r);
+ }
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_primary(DB_ENV *env, DB *db, int ndbs, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, 0);
+ int v[ndbs]; get_data(v, i, ndbs);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, &v[0], sizeof v);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+populate_secondary(DB_ENV *env, DB *db, int dbnum, int nrows) {
+ int r;
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, NULL, &txn, 0); assert_zero(r);
+
+ // populate
+ for (int i = 0; i < nrows; i++) {
+ int k = get_key(i, dbnum);
+ DBT key; dbt_init(&key, &k, sizeof k);
+ DBT val; dbt_init(&val, NULL, 0);
+ r = db->put(db, txn, &key, &val, 0); assert_zero(r);
+ }
+
+ r = txn->commit(txn, 0); assert_zero(r);
+}
+
+static void
+run_test(int ndbs, int nrows) {
+ int r;
+ DB_ENV *env = NULL;
+ r = db_env_create(&env, 0); assert_zero(r);
+
+ r = env->set_generate_row_callback_for_put(env, put_callback); assert_zero(r);
+ r = env->set_generate_row_callback_for_del(env, del_callback); assert_zero(r);
+
+ r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ DB *db[ndbs];
+ for (int dbnum = 0; dbnum < ndbs; dbnum++) {
+ r = db_create(&db[dbnum], env, 0); assert_zero(r);
+
+ DBT dbt_dbnum; dbt_init(&dbt_dbnum, &dbnum, sizeof dbnum);
+
+ char dbname[32]; sprintf(dbname, "%d.tdb", dbnum);
+ r = db[dbnum]->open(db[dbnum], NULL, dbname, NULL, DB_BTREE, DB_AUTO_COMMIT+DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = db[dbnum]->change_descriptor(db[dbnum], txn_desc, &dbt_dbnum, 0); CKERR(chk_r); }
+ });
+ }
+
+ for (int dbnum = 0; dbnum < ndbs-1; dbnum++) {
+ if (dbnum == 0)
+ populate_primary(env, db[dbnum], ndbs, nrows);
+ else
+ populate_secondary(env, db[dbnum], dbnum, nrows);
+ }
+
+ DB_TXN *indexer_txn = NULL;
+ r = env->txn_begin(env, NULL, &indexer_txn, 0); assert_zero(r);
+
+ DB_INDEXER *indexer = NULL;
+ uint32_t db_flags = 0;
+ r = env->create_indexer(env, indexer_txn, &indexer, db[0], 1, &db[ndbs-1], &db_flags, 0); assert_zero(r);
+
+ update_diagonal(env, db, ndbs, nrows);
+
+ r = indexer->build(indexer); assert_zero(r);
+ r = indexer->close(indexer); assert_zero(r);
+
+ r = indexer_txn->commit(indexer_txn, 0); assert_zero(r);
+
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ verify_seq(env, db[dbnum], dbnum, ndbs, nrows);
+ for (int dbnum = 0; dbnum < ndbs; dbnum++)
+ r = db[dbnum]->close(db[dbnum], 0); assert_zero(r);
+
+ r = env->close(env, 0); assert_zero(r);
+}
+
+int
+test_main(int argc, char * const argv[]) {
+ int r;
+ int ndbs = 2;
+ int nrows = 2;
+
+ // parse_args(argc, argv);
+ for (int i = 1; i < argc; i++) {
+ char * const arg = argv[i];
+ if (strcmp(arg, "-v") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(arg, "-q") == 0) {
+ verbose = 0;
+ continue;
+ }
+ if (strcmp(arg, "--ndbs") == 0 && i+1 < argc) {
+ ndbs = atoi(argv[++i]);
+ continue;
+ }
+ if (strcmp(arg, "--nrows") == 0 && i+1 < argc) {
+ nrows = atoi(argv[++i]);
+ continue;
+ }
+ }
+
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); assert_zero(r);
+
+ run_test(ndbs, nrows);
+
+ return 0;
+}
+
diff --git a/storage/tokudb/PerconaFT/src/tests/update.cc b/storage/tokudb/PerconaFT/src/tests/update.cc
new file mode 100644
index 00000000000..e569660525c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/update.cc
@@ -0,0 +1,91 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* test the update functionality. */
+
+#include "test.h"
+
+DB_ENV *env;
+
+// the commands are: byte 1 is "nop" "add" or "del". Byte 2 is the amount to add.
+enum cmd { CNOP, CADD, CDEL };
+
+static int increment_update (DB *db __attribute__((__unused__)),
+ const DBT *key __attribute__((__unused__)),
+ const DBT *old_val, const DBT *extra,
+ void (*set_val)(const DBT *new_val,
+ void *set_extra),
+ void *set_extra) {
+ assert (extra->size==2);
+ assert (old_val->size==4);
+ unsigned char *CAST_FROM_VOIDP(extra_data, extra->data);
+ switch ((enum cmd)(extra_data[0])) {
+ case CNOP:
+ return 0;
+ case CADD: {
+ unsigned int data = *(unsigned int*)old_val->data;
+ data += extra_data[1];
+ DBT new_val = {.data=&data, .size=4, .ulen=0, .flags=0};
+ set_val(&new_val, set_extra);
+ return 0;
+ }
+ case CDEL:
+ set_val(NULL, set_extra);
+ return 0;
+ }
+ assert(0); return 0; // enumeration failed.
+}
+
+static void setup (void) {
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ { int r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r); }
+ { int r=db_env_create(&env, 0); CKERR(r); }
+ env->set_errfile(env, stderr);
+ env->set_update(env, increment_update);
+}
+
+static void cleanup (void) {
+ { int r = env->close(env, 0); CKERR(r); }
+}
+
+int test_main (int argc __attribute__((__unused__)), char *const argv[] __attribute__((__unused__))) {
+
+ setup();
+ cleanup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/upgrade-test-1.cc b/storage/tokudb/PerconaFT/src/tests/upgrade-test-1.cc
new file mode 100644
index 00000000000..099aa0b6925
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/upgrade-test-1.cc
@@ -0,0 +1,263 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+
+#define kv_pair_funcs 1 // pull in kv_pair generators from test.h
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+#include "test_kv_gen.h"
+
+/*
+ */
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+int NUM_DBS=5;
+int NUM_ROWS=100000;
+int SRC_VERSION = 4;
+int littlenode = 0;
+int flat = 0;
+
+#define OLDDATADIR "../../../../tokudb.data/"
+
+char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+char *db_v5_dir = "dir.preload-db.c.tdb";
+char *db_v4_dir = OLDDATADIR "env_preload.4.2.0.cleanshutdown";
+char *db_v4_dir_node4k = OLDDATADIR "env_preload.4.2.0.node4k.cleanshutdown";
+char *db_v4_dir_flat = OLDDATADIR "env_preload.4.2.0.flat.cleanshutdown";
+
+// HACK: Newer versions of the database/ft to use with this old
+// upgrade test code.
+char *db_v6_dir = OLDDATADIR "env_preload.5.0.8.cleanshutdown";
+char *db_v6_dir_node4k = OLDDATADIR "env_preload.5.0.8.node4k.cleanshutdown";
+char *db_v6_dir_flat = OLDDATADIR "env_preload.5.0.8.flat.cleanshutdown";
+
+char *db_v7_dir = OLDDATADIR "env_preload.5.2.7.cleanshutdown";
+char *db_v7_dir_node4k = OLDDATADIR "env_preload.5.2.7.node4k.cleanshutdown";
+char *db_v7_dir_flat = OLDDATADIR "env_preload.5.2.7.flat.cleanshutdown";
+
+
+// should put this in test.h:
+static __attribute__((__unused__)) int
+char_dbt_cmp (const DBT *a, const DBT *b) {
+ int rval = 0;
+ assert(a && b);
+ if (a->size < b->size) rval = -1;
+ else if (a->size > b->size) rval = 1;
+ else if (a->size) { // if both strings are of size zero, return 0
+ rval = strcmp((char*)a->data, (char*)b->data);
+ }
+ return rval;
+}
+
+
+static void upgrade_test_1(DB **dbs) {
+ int r;
+ // open the DBS
+ {
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ r = char_dbt_cmp(&desc, &(dbs[i]->descriptor->dbt));
+ CKERR(r); // verify that upgraded descriptor is same as original
+ }
+ }
+
+ // read and verify all rows
+ {
+ if ( verbose ) {printf("checking");fflush(stdout);}
+ check_results(env, dbs, NUM_DBS, NUM_ROWS);
+ if ( verbose) {printf("\ndone\n");fflush(stdout);}
+ }
+ // close
+ {
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ }
+}
+
+static void setup(void) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+ char * src_db_dir;
+
+ if ( SRC_VERSION == 4 ) {
+ if (flat)
+ src_db_dir = db_v4_dir_flat;
+ else if (littlenode)
+ src_db_dir = db_v4_dir_node4k;
+ else
+ src_db_dir = db_v4_dir;
+ }
+ else if ( SRC_VERSION == 5 ) {
+ src_db_dir = db_v5_dir;
+ }
+ else if (SRC_VERSION == 6) {
+ if (flat) {
+ src_db_dir = db_v6_dir_flat;
+ } else if (littlenode) {
+ src_db_dir = db_v6_dir_node4k;
+ } else {
+ src_db_dir = db_v6_dir;
+ }
+ }
+ else if (SRC_VERSION == 7) {
+ if (flat) {
+ src_db_dir = db_v7_dir_flat;
+ } else if (littlenode) {
+ src_db_dir = db_v7_dir_node4k;
+ } else {
+ src_db_dir = db_v7_dir;
+ }
+ }
+ else {
+ fprintf(stderr, "unsupported PerconaFT version %d to upgrade\n", SRC_VERSION);
+ assert(0);
+ }
+
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+ generate_permute_tables();
+
+}
+
+static void run_test(void)
+{
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ if (littlenode) {
+ r = env->set_cachesize(env, 0, 512*1024, 1); CKERR(r);
+ }
+ r = env->set_redzone(env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 1); CKERR(r);
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+
+ // --------------------------
+ upgrade_test_1(dbs);
+ // --------------------------
+
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ if (SRC_VERSION == 4) {
+ littlenode = 1; // 4k nodes, small cache
+ }
+ setup();
+ run_test(); // read, upgrade, write back to disk
+ run_test(); // read and verify
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -d <num_dbs> -r <num_rows> %s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-V")==0) {
+ argc--; argv++;
+ SRC_VERSION = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-f")==0) {
+ flat = 1;
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/upgrade-test-2.cc b/storage/tokudb/PerconaFT/src/tests/upgrade-test-2.cc
new file mode 100644
index 00000000000..0a0d5d085b6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/upgrade-test-2.cc
@@ -0,0 +1,244 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+#include "test_kv_gen.h"
+
+/*
+ */
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+int NUM_DBS=5;
+int NUM_ROWS=100000;
+int CHECK_RESULTS=0;
+int SRC_VERSION = 4;
+int littlenode = 0;
+
+#define OLDDATADIR "../../../../tokudb.data/"
+
+char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+char *db_v5_dir = "dir.preload-db.c.tdb";
+char *db_v4_dir = OLDDATADIR "env_preload.4.2.0.cleanshutdown";
+char *db_v4_dir_node4k = OLDDATADIR "env_preload.4.2.0.node4k.cleanshutdown";
+
+
+static void upgrade_test_2(DB **dbs) {
+ int r = 0;
+ // open the DBS
+ {
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+ }
+ // close
+ {
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ }
+ // open
+ {
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+ }
+
+ // read and verify all rows
+ {
+ if ( verbose ) {printf("checking");fflush(stdout);}
+ check_results(env, dbs, NUM_DBS, NUM_ROWS);
+ if ( verbose) {printf("\ndone\n");fflush(stdout);}
+ }
+ // close
+ {
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ }
+}
+
+
+static void setup(void) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+ char * src_db_dir;
+
+ if ( SRC_VERSION == 4 ) {
+ if (littlenode)
+ src_db_dir = db_v4_dir_node4k;
+ else
+ src_db_dir = db_v4_dir;
+ }
+ else if ( SRC_VERSION == 5 ) {
+ src_db_dir = db_v5_dir;
+ }
+ else {
+ fprintf(stderr, "unsupported PerconaFT version %d to upgrade\n", SRC_VERSION);
+ assert(0);
+ }
+
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+ generate_permute_tables();
+
+}
+
+static void run_test(int checkpoint_period)
+{
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ if (littlenode) {
+ r = env->set_cachesize(env, 0, 512*1024, 1); CKERR(r);
+ }
+ r = env->set_redzone(env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, checkpoint_period); CKERR(r);
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+
+ // --------------------------
+ upgrade_test_2(dbs);
+ // --------------------------
+
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ if (SRC_VERSION == 4) {
+ littlenode = 1; // 4k nodes, small cache
+ }
+ setup();
+ run_test(1);
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -c -d <num_dbs> -r <num_rows> %s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-c")==0) {
+ CHECK_RESULTS = 1;
+ } else if (strcmp(argv[0], "-V")==0) {
+ argc--; argv++;
+ SRC_VERSION = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/upgrade-test-3.cc b/storage/tokudb/PerconaFT/src/tests/upgrade-test-3.cc
new file mode 100644
index 00000000000..b80e600d86a
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/upgrade-test-3.cc
@@ -0,0 +1,260 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Purpose of this test is to verify that dictionaries created with 4.2.0
+// can be properly truncated with PerconaFT version 5.x or later.
+
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+#include "test_kv_gen.h"
+
+/*
+ */
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+int NUM_DBS=5;
+int NUM_ROWS=100000;
+int CHECK_RESULTS=0;
+int SRC_VERSION = 4;
+int littlenode = 0;
+
+#define OLDDATADIR "../../../../tokudb.data/"
+
+char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+char *db_v5_dir = "dir.preload-db.c.tdb";
+char *db_v4_dir = OLDDATADIR "env_preload.4.2.0.cleanshutdown";
+char *db_v4_dir_node4k = OLDDATADIR "env_preload.4.2.0.node4k.cleanshutdown";
+
+
+static void upgrade_test_3(DB **dbs) {
+ int r = 0;
+ char name[MAX_NAME*2];
+
+ // truncate, verify, close, open, verify again
+ DBC *cursor;
+ DB_TXN * txn;
+ DBT desc;
+ int idx[MAX_DBS];
+
+ dbt_init(&desc, "foo", sizeof("foo"));
+
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+
+ r = env->txn_begin(env, NULL, &txn, DB_SERIALIZABLE);
+ CKERR(r);
+
+ // truncate the tree
+ uint32_t row_count = 0;
+ r = dbs[i]->truncate(dbs[i], 0, &row_count, 0); assert(r == 0);
+
+ // walk the tree - expect 0 rows
+ int rowcount = 0;
+ r = dbs[i]->cursor(dbs[i], txn, &cursor, 0);
+ CKERR(r);
+ while (1) {
+ DBT key, val;
+ r = cursor->c_get(cursor, dbt_init(&key, 0, 0), dbt_init(&val, 0, 0), DB_NEXT);
+ if (r == DB_NOTFOUND) break;
+ rowcount++;
+ }
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ assert(rowcount == 0);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = dbs[i]->close(dbs[i], 0); assert(r == 0);
+
+ r = db_create(&dbs[i], env, 0); assert(r == 0);
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+
+ // open new txn and walk the tree again - expect 0 rows
+
+ r = env->txn_begin(env, NULL, &txn, DB_SERIALIZABLE);
+ CKERR(r);
+
+ rowcount = 0;
+ r = dbs[i]->cursor(dbs[i], txn, &cursor, 0); assert(r == 0);
+ while (1) {
+ DBT key, val;
+ r = cursor->c_get(cursor, dbt_init(&key, 0, 0), dbt_init(&val, 0, 0), DB_NEXT);
+ if (r == DB_NOTFOUND) break;
+ rowcount++;
+ }
+ r = cursor->c_close(cursor); assert(r == 0);
+ assert(rowcount == 0);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = dbs[i]->close(dbs[i], 0);
+ CKERR(r);
+
+ dbs[i] = NULL;
+ }
+
+}
+
+
+static void setup(void) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+ char * src_db_dir;
+
+ if ( SRC_VERSION == 4 ) {
+ if (littlenode)
+ src_db_dir = db_v4_dir_node4k;
+ else
+ src_db_dir = db_v4_dir;
+ }
+ else if ( SRC_VERSION == 5 ) {
+ src_db_dir = db_v5_dir;
+ }
+ else {
+ fprintf(stderr, "unsupported PerconaFT version %d to upgrade\n", SRC_VERSION);
+ assert(0);
+ }
+
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+}
+
+static void run_test(int checkpoint_period)
+{
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ if (littlenode) {
+ r = env->set_cachesize(env, 0, 512*1024, 1); CKERR(r);
+ }
+ r = env->set_redzone(env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, checkpoint_period); CKERR(r);
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+
+ // --------------------------
+ upgrade_test_3(dbs);
+ // --------------------------
+
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ if (SRC_VERSION == 4) {
+ littlenode = 1; // 4k nodes, small cache
+ }
+ setup();
+ run_test(1);
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -d <num_dbs> -V <version> %s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-V")==0) {
+ argc--; argv++;
+ SRC_VERSION = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/upgrade-test-4.cc b/storage/tokudb/PerconaFT/src/tests/upgrade-test-4.cc
new file mode 100644
index 00000000000..79627952aef
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/upgrade-test-4.cc
@@ -0,0 +1,364 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+#include "test_kv_gen.h"
+
+/****************************************************************************************
+ *
+ * Test sequence is run four times, two in outer loop, two in inner loop
+ * Outer loop is for default or small node and cachetable sizes,
+ * inner loop is for insert and delete.
+ *
+ * open dbs
+ * read and verify first n rows of primary, a few interspersed rows of secondaries (n is very small so only a few nodes of secondaries are upgraded, even with prefetch)
+ * close dbs (dictionaries now partially upgraded)
+ * open dbs
+ * read and verify a few more rows of primary, a few more interspersed rows of secondaries
+ * close dbs (some more nodes now upgraded)
+ * open dbs
+ * if (insert test)
+ * insert at end of primary and interspersed in secondary dictionaries
+ * else (delete test)
+ * delete from beginning of primary and interspersed in secondary dictionaries
+ * close dbs
+ * open dbs
+ * verify all rows (including newly inserted ones)
+ * close dbs
+ *
+ */
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+int NUM_DBS=5;
+int NUM_ROWS=100000;
+int CHECK_RESULTS=0;
+int SRC_VERSION = 4;
+int littlenode = 0;
+
+#define OLDDATADIR "../../../../tokudb.data/"
+
+char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+char *db_v5_dir = "dir.preload-db.c.tdb";
+char *db_v4_dir = OLDDATADIR "env_preload.4.2.0.cleanshutdown";
+char *db_v4_dir_node4k = OLDDATADIR "env_preload.4.2.0.node4k.cleanshutdown";
+
+
+enum {ROWS_PER_TRANSACTION=10000};
+
+static int idx[MAX_DBS];
+
+typedef enum {insert, delete} test_type;
+
+static void
+open_dbs(DB **dbs) {
+ int r;
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+}
+
+
+static void
+close_dbs(DB **dbs) {
+ for(int i=0;i<NUM_DBS;i++) {
+ int r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+}
+
+
+static void upgrade_test_4(DB **dbs, test_type test_to_do) {
+ int r;
+ int n = 4; // number of rows to check to partially upgrade dictionary
+ char * msg;
+ if (test_to_do == insert)
+ msg = "insert";
+ else if (test_to_do == delete)
+ msg = "delete";
+ else assert(0);
+
+
+ // open the DBS
+ open_dbs(dbs);
+
+ // check first few rows of primary, some (pseudo)random rows of secondaries
+ {
+ check_results(env, dbs, NUM_DBS, n);
+ if (verbose)
+ printf("First %d rows checked, now close and reopen\n", n);
+ }
+
+ // close and reopen
+ close_dbs(dbs);
+ open_dbs(dbs);
+
+ // check first few rows of primary, some (pseudo)random rows of secondaries
+ {
+ n *= 2;
+ check_results(env, dbs, NUM_DBS, n);
+ if (verbose)
+ printf("\nFirst %d rows checked, now %s some rows\n", n, msg);
+ }
+
+ // close and reopen
+ close_dbs(dbs);
+ open_dbs(dbs);
+
+ // insert or delete some rows
+ DB_TXN *txn;
+ DBT skey, sval;
+ DBT key, val;
+ dbt_init_realloc(&key);
+ dbt_init_realloc(&val);
+
+ unsigned int k, v;
+ if ( verbose ) {
+ printf("%s some rows\n", msg);
+ fflush(stdout);
+ }
+ int num_rows_to_modify, base;
+ if (test_to_do == insert) {
+ num_rows_to_modify = NUM_ROWS;
+ base = NUM_ROWS; // insert after existing rows in primary
+ }
+ else if (test_to_do == delete) {
+ num_rows_to_modify = 2*n;
+ base = 0; // delete some rows from primary
+ }
+ else assert(0);
+ int outer_loop_num = ( num_rows_to_modify <= ROWS_PER_TRANSACTION ) ? 1 : (num_rows_to_modify / ROWS_PER_TRANSACTION);
+ for(int x=0;x<outer_loop_num;x++) {
+ r = env->txn_begin(env, NULL, &txn, 0); CKERR(r);
+ for(int i=1; (i<=ROWS_PER_TRANSACTION && i<=num_rows_to_modify); i++) {
+ k = i + (x*ROWS_PER_TRANSACTION) + base;
+ v = generate_val(k, 0);
+ dbt_init(&skey, &k, sizeof(unsigned int));
+ dbt_init(&sval, &v, sizeof(unsigned int));
+
+ for(int db = 0;db < NUM_DBS;db++) {
+ put_multiple_generate(dbs[db], // dest_db
+ NULL, // src_db, ignored
+ &key, &val, //
+ &skey, &sval, // src_key, src_val
+ NULL); // extra, ignored
+ if (test_to_do == insert) {
+ r = dbs[db]->put(dbs[db], txn, &key, &val, 0);
+ CKERR(r);
+ }
+ else if (test_to_do == delete) {
+ r = dbs[db]->del(dbs[db], txn, &key, 0);
+ CKERR(r);
+ }
+ else assert(0);
+
+ if (key.flags == 0) { dbt_init_realloc(&key); }
+ if (val.flags == 0) { dbt_init_realloc(&val); }
+ }
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+ if ( verbose ) {printf(".");fflush(stdout);}
+ }
+ if ( key.flags ) { toku_free(key.data); key.data = NULL; }
+ if ( val.flags ) { toku_free(val.data); key.data = NULL; }
+
+ // close
+ close_dbs(dbs);
+
+ // open
+ open_dbs(dbs);
+
+ // read and verify all rows
+ {
+ if ( verbose ) {printf("\nchecking");fflush(stdout);}
+ if (test_to_do == insert)
+ check_results(env, dbs, NUM_DBS, NUM_ROWS * 2);
+ else if (test_to_do == delete)
+ check_results_after_row_n(env, dbs, NUM_DBS, NUM_ROWS, num_rows_to_modify);
+ else assert(0);
+ if ( verbose) {printf("\ndone\n");fflush(stdout);}
+ }
+ // close
+ {
+ for(int i=0;i<NUM_DBS;i++) {
+ r = dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ }
+}
+
+static void setup(void) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+ char * src_db_dir;
+
+ if ( SRC_VERSION == 4 ) {
+ if (littlenode)
+ src_db_dir = db_v4_dir_node4k;
+ else
+ src_db_dir = db_v4_dir;
+ }
+ else if ( SRC_VERSION == 5 ) {
+ src_db_dir = db_v5_dir;
+ }
+ else {
+ fprintf(stderr, "unsupported PerconaFT version %d to upgrade\n", SRC_VERSION);
+ assert(0);
+ }
+
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+ generate_permute_tables();
+
+}
+
+static void run_test(test_type test_to_do)
+{
+ int r;
+
+ r = db_env_create(&env, 0); CKERR(r);
+ if (littlenode) {
+ r = env->set_cachesize(env, 0, 512*1024, 1); CKERR(r);
+ }
+ r = env->set_redzone(env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 5); CKERR(r);
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+
+ // --------------------------
+ upgrade_test_4(dbs, test_to_do);
+ // --------------------------
+
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ littlenode = 0;
+ setup();
+ run_test(insert);
+ setup();
+ run_test(delete);
+ if (SRC_VERSION == 4) {
+ if (verbose)
+ printf("Now repeat test with small nodes and small cache.\n");
+ littlenode = 1; // 4k nodes, small cache
+ setup();
+ run_test(insert);
+ setup();
+ run_test(delete);
+ }
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -c -d <num_dbs> -r <num_rows> %s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-c")==0) {
+ CHECK_RESULTS = 1;
+ } else if (strcmp(argv[0], "-V")==0) {
+ argc--; argv++;
+ SRC_VERSION = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/upgrade-test-5.cc b/storage/tokudb/PerconaFT/src/tests/upgrade-test-5.cc
new file mode 100644
index 00000000000..555a5c195cc
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/upgrade-test-5.cc
@@ -0,0 +1,245 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#define kv_pair_funcs 1 // pull in kv_pair generators from test.h
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+#include "test_kv_gen.h"
+
+/*
+ */
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+int NUM_DBS=1;
+int NUM_ROWS=100000;
+int SRC_VERSION = 4;
+
+#define MAXDEPTH 64
+#define OLDDATADIR "../../../../tokudb.data/"
+
+char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+char *db_v5_dir = "dir.preload-db-nested.c.tdb";
+char *db_v4_dir = OLDDATADIR "env_preload.4.2.0.nested.cleanshutdown";
+
+
+static void
+check_results_nested(DB ** dbs, const uint num_rows) {
+ int num_dbs = 1; // maybe someday increase
+ for(int j=0;j<num_dbs;j++){
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ int r;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+ for(uint i=0;i<num_rows;i++) {
+ if (i % MAXDEPTH) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ CKERR(r);
+ uint observed_k = *(unsigned int*)key.data;
+ uint observed_v = *(unsigned int*)val.data;
+ uint expected_k = i;
+ uint generated_value = generate_val(i, 0);
+ uint expected_v = generated_value + (i%MAXDEPTH - 1);
+ if (verbose >= 3)
+ printf("expected key %d, observed key %d, expected val %d, observed val %d\n",
+ expected_k, observed_k, expected_v, observed_v);
+ // test that we have the expected keys and values
+ assert(observed_k == expected_k);
+ assert(observed_v == expected_v);
+ }
+ dbt_init(&key, NULL, sizeof(unsigned int));
+ dbt_init(&val, NULL, sizeof(unsigned int));
+ if ( verbose && (i%10000 == 0)) {printf("."); fflush(stdout);}
+ }
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ if ( verbose ) {printf("ok");fflush(stdout);}
+}
+
+
+static void upgrade_test_1(DB **dbs) {
+ int r;
+ // open the DBS
+ {
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+ }
+
+ // read and verify all rows
+ {
+ if ( verbose ) {printf("checking");fflush(stdout);}
+ check_results_nested(&dbs[0], NUM_ROWS);
+ if ( verbose) {printf("\ndone\n");fflush(stdout);}
+ }
+ // close
+ {
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ }
+}
+
+static void setup(void) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+ char * src_db_dir;
+
+ if ( SRC_VERSION == 4 ) {
+ src_db_dir = db_v4_dir;
+ }
+ else if ( SRC_VERSION == 5 ) {
+ src_db_dir = db_v5_dir;
+ }
+ else {
+ fprintf(stderr, "unsupported PerconaFT version %d to upgrade\n", SRC_VERSION);
+ assert(0);
+ }
+
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+ generate_permute_tables();
+
+}
+
+static void run_test(void)
+{
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ r = env->set_cachesize(env, 0, 512*1024, 1); CKERR(r);
+ r = env->set_redzone(env, 0); CKERR(r);
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 1); CKERR(r);
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+
+ // --------------------------
+ upgrade_test_1(dbs);
+ // --------------------------
+
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+
+}
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+ setup();
+ run_test(); // read, upgrade, write back to disk
+ run_test(); // read and verify
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -r <num_rows> %s\n", cmd);
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-V")==0) {
+ argc--; argv++;
+ SRC_VERSION = atoi(argv[0]);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/upgrade-test-6.cc b/storage/tokudb/PerconaFT/src/tests/upgrade-test-6.cc
new file mode 100644
index 00000000000..d3e0154cd30
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/upgrade-test-6.cc
@@ -0,0 +1,416 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Purpose of this test is to verify that a dictionary created by the 4.2.0
+// loader can be properly read with 5.0.
+// This file was derived from the 4.2.0 version of loader-stress-test.c,
+// which was used to create the dictionary.
+// This test only reads (and upgrades) the dictionary, it does not load it.
+
+// Need to use malloc for the malloc instrumentation tests
+#define TOKU_ALLOW_DEPRECATED
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+DB_ENV *env;
+enum {MAX_NAME=128};
+enum {MAX_DBS=256};
+int NUM_DBS=1;
+int NUM_ROWS=250000;
+int CHECK_RESULTS=0;
+int USE_PUTS=0;
+enum { old_default_cachesize=1024 }; // MB
+int CACHESIZE=old_default_cachesize;
+int ALLOW_DUPS=0;
+enum {MAGIC=311};
+char *datadir = NULL;
+bool check_est = true; // do check the estimates by default
+bool footprint_print = false; // print memory footprint info
+
+
+// Code for showing memory footprint information.
+pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER;
+size_t hiwater;
+size_t water;
+size_t hiwater_start;
+static long long mcount = 0, fcount=0;
+
+
+size_t malloc_usable_size(void *p);
+
+static void my_free(void*p) {
+ if (p) {
+ water-=malloc_usable_size(p);
+ }
+ free(p);
+}
+
+static void *my_malloc(size_t size) {
+ void *r = malloc(size);
+ if (r) {
+ water += malloc_usable_size(r);
+ if (water>hiwater) hiwater=water;
+ }
+ return r;
+}
+
+static void *my_realloc(void *p, size_t size) {
+ size_t old_usable = p ? malloc_usable_size(p) : 0;
+ void *r = realloc(p, size);
+ if (r) {
+ water -= old_usable;
+ water += malloc_usable_size(r);
+ }
+ return r;
+}
+
+//
+// Functions to create unique key/value pairs, row generators, checkers, ... for each of NUM_DBS
+//
+
+// a is the bit-wise permute table. For DB[i], permute bits as described in a[i] using 'twiddle32'
+// inv is the inverse bit-wise permute of a[]. To get the original value from a twiddled value, twiddle32 (again) with inv[]
+int a[MAX_DBS][32];
+int inv[MAX_DBS][32];
+
+
+// rotate right and left functions
+static inline unsigned int rotr32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x >> n) | ( x << (32 - n));
+}
+static inline unsigned int rotl32(const unsigned int x, const unsigned int num) {
+ const unsigned int n = num % 32;
+ return (x << n) | ( x >> (32 - n));
+}
+
+static void generate_permute_tables(void) {
+ int i, j, tmp;
+ for(int db=0;db<MAX_DBS;db++) {
+ for(i=0;i<32;i++) {
+ a[db][i] = i;
+ }
+ for(i=0;i<32;i++) {
+ j = random() % (i + 1);
+ tmp = a[db][j];
+ a[db][j] = a[db][i];
+ a[db][i] = tmp;
+ }
+// if(db < NUM_DBS){ printf("a[%d] = ", db); for(i=0;i<32;i++) { printf("%2d ", a[db][i]); } printf("\n");}
+ for(i=0;i<32;i++) {
+ inv[db][a[db][i]] = i;
+ }
+ }
+}
+
+// permute bits of x based on inverse permute table bitmap
+static unsigned int inv_twiddle32(unsigned int x, int db)
+{
+ unsigned int b = 0;
+ for(int i=0;i<32;i++) {
+ b |= (( x >> i ) & 1) << inv[db][i];
+ }
+ return b;
+}
+
+
+static unsigned int pkey_for_val(int key, int i) {
+ return rotr32(key, i) - MAGIC;
+}
+
+
+static void check_results(DB **dbs)
+{
+ for(int j=0;j<NUM_DBS;j++){
+ DBT key, val;
+ unsigned int k=0, v=0;
+ dbt_init(&key, &k, sizeof(unsigned int));
+ dbt_init(&val, &v, sizeof(unsigned int));
+ int r;
+ unsigned int pkey_for_db_key;
+
+ DB_TXN *txn;
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+
+ DBC *cursor;
+ r = dbs[j]->cursor(dbs[j], txn, &cursor, 0);
+ CKERR(r);
+ for(int i=0;i<NUM_ROWS;i++) {
+ r = cursor->c_get(cursor, &key, &val, DB_NEXT);
+ CKERR(r);
+ k = *(unsigned int*)key.data;
+ pkey_for_db_key = (j == 0) ? k : inv_twiddle32(k, j);
+ v = *(unsigned int*)val.data;
+ // test that we have the expected keys and values
+ assert((unsigned int)pkey_for_db_key == (unsigned int)pkey_for_val(v, j));
+// printf(" DB[%d] key = %10u, val = %10u, pkey_for_db_key = %10u, pkey_for_val=%10d\n", j, v, k, pkey_for_db_key, pkey_for_val(v, j));
+ }
+ if (verbose) {
+ printf(".");
+ fflush(stdout);
+ }
+ r = cursor->c_close(cursor);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+ if (verbose)
+ printf("\nCheck OK\n");
+}
+
+static void *expect_poll_void = &expect_poll_void;
+
+static struct progress_info {
+ double time;
+ double progress;
+} *progress_infos=NULL;
+static int progress_infos_count=0;
+
+static void test_loader(DB **dbs)
+{
+ int r;
+ DB_TXN *txn;
+
+
+ // this is the essential part of the upgrade test
+ check_results(dbs);
+
+ for (int i=0; i<NUM_DBS; i++) {
+ r = env->txn_begin(env, NULL, &txn, 0);
+ CKERR(r);
+ DB_BTREE_STAT64 stats;
+ r = dbs[i]->stat64(dbs[i], txn, &stats);
+ CKERR(r);
+ if (verbose)
+ printf("n_keys=%" PRIu64 " n_data=%" PRIu64 " dsize=%" PRIu64 " fsize=%" PRIu64 "\n",
+ stats.bt_nkeys, stats.bt_ndata, stats.bt_dsize, stats.bt_fsize);
+ assert(stats.bt_nkeys == (uint64_t)NUM_ROWS);
+ assert(stats.bt_ndata == (uint64_t)NUM_ROWS);
+ assert(stats.bt_dsize == ((uint64_t)NUM_ROWS) * 2 * sizeof(unsigned int));
+ r = txn->commit(txn, 0);
+ CKERR(r);
+ }
+}
+
+
+char *free_me = NULL;
+char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+char *tmp_subdir = "tmp.subdir";
+
+#define OLDDATADIR "../../../../tokudb.data/"
+char *db_v4_dir = OLDDATADIR "env_preload.4.2.0.loader250kd1.cleanshutdown";
+
+
+static void setup(void) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+ char * src_db_dir;
+
+ src_db_dir = db_v4_dir;
+
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+}
+
+
+
+
+static void run_test(void)
+{
+ int r;
+
+ setup();
+ {
+ char len = strlen(env_dir) + strlen(tmp_subdir) + 10;
+ char tmpdir[len];
+ r = snprintf(tmpdir, len, "%s/%s", env_dir, tmp_subdir);
+ assert(r<len);
+ r = db_env_create(&env, 0); CKERR(r);
+ r = env->set_tmp_dir(env, tmp_subdir); CKERR(r);
+ }
+ r = env->set_default_bt_compare(env, uint_dbt_cmp); CKERR(r);
+ if ( verbose ) printf("CACHESIZE = %d MB\n", CACHESIZE);
+ r = env->set_cachesize(env, CACHESIZE / 1024, (CACHESIZE % 1024)*1024*1024, 1); CKERR(r);
+ if (datadir) {
+ r = env->set_data_dir(env, datadir); CKERR(r);
+ }
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->checkpointing_set_period(env, 60); CKERR(r);
+
+ DBT desc;
+ dbt_init(&desc, "foo", sizeof("foo"));
+ char name[MAX_NAME*2];
+
+ DB **dbs = (DB**)toku_malloc(sizeof(DB*) * NUM_DBS);
+ assert(dbs != NULL);
+ int idx[MAX_DBS];
+ for(int i=0;i<NUM_DBS;i++) {
+ idx[i] = i;
+ r = db_create(&dbs[i], env, 0); CKERR(r);
+ dbs[i]->app_private = &idx[i];
+ snprintf(name, sizeof(name), "db_%04x", i);
+ r = dbs[i]->open(dbs[i], NULL, name, NULL, DB_BTREE, DB_CREATE, 0666); CKERR(r);
+ IN_TXN_COMMIT(env, NULL, txn_desc, 0, {
+ { int chk_r = dbs[i]->change_descriptor(dbs[i], txn_desc, &desc, 0); CKERR(chk_r); }
+ });
+ }
+
+ generate_permute_tables();
+
+ // -------------------------- //
+ test_loader(dbs);
+ // -------------------------- //
+
+ for(int i=0;i<NUM_DBS;i++) {
+ dbs[i]->close(dbs[i], 0); CKERR(r);
+ dbs[i] = NULL;
+ }
+ if (verbose >= 2)
+ print_engine_status(env);
+ r = env->close(env, 0); CKERR(r);
+ toku_free(dbs);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+
+ run_test();
+ if (free_me) toku_free(free_me);
+
+ if (progress_infos) {
+ if (verbose>=2) {
+ double ratio=progress_infos[progress_infos_count-1].time/progress_infos[progress_infos_count-1].progress;
+ printf("Progress ratios:\n");
+ for (int i=0; i<progress_infos_count; i++) {
+ printf(" %5.3f\n", (progress_infos[i].time/progress_infos[i].progress)/ratio);
+ }
+ }
+ toku_free(progress_infos);
+ }
+ if (footprint_print) {
+ printf("%s:%d Hiwater=%ld water=%ld (extra hiwater=%ldM) mcount=%lld fcount=%lld\n", __FILE__, __LINE__, hiwater, water, (hiwater-hiwater_start)/(1024*1024), mcount, fcount);
+ extern void malloc_stats(void);
+ malloc_stats();
+ }
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+
+ // Must look for "-f" right away before we malloc anything.
+ for (int i=1; i<argc; i++) {
+
+ if (strcmp(argv[i], "-f")) {
+ db_env_set_func_malloc(my_malloc);
+ db_env_set_func_realloc(my_realloc);
+ db_env_set_func_free(my_free);
+ }
+ }
+
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ CACHESIZE = (toku_os_get_phys_memory_size() / (1024*1024))/2; //MB
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -d <num_dbs> -r <num_rows> [-m <megabytes>] [-M]\n%s\n", cmd);
+ fprintf(stderr, " where -d <num_dbs> is the number of dictionaries to build (primary & secondary). (Default=%d)\n", NUM_DBS);
+ fprintf(stderr, " -m <m> use m MB of memory for the cachetable (default is %d MB)\n", CACHESIZE);
+ fprintf(stderr, " -M use %d MB of memory for the cachetable\n", old_default_cachesize);
+ fprintf(stderr, " -f print memory footprint information at various points in the load\n");
+ exit(resultcode);
+ } else if (strcmp(argv[0], "-d")==0) {
+ argc--; argv++;
+ NUM_DBS = atoi(argv[0]);
+ if ( NUM_DBS > MAX_DBS ) {
+ fprintf(stderr, "max value for -d field is %d\n", MAX_DBS);
+ resultcode=1;
+ goto do_usage;
+ }
+ } else if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-f")==0) {
+ footprint_print = true;
+ } else if (strcmp(argv[0], "-r")==0) {
+ argc--; argv++;
+ NUM_ROWS = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-m")==0) {
+ argc--; argv++;
+ CACHESIZE = atoi(argv[0]);
+ } else if (strcmp(argv[0], "-M")==0) {
+ CACHESIZE = old_default_cachesize;
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/upgrade-test-7.cc b/storage/tokudb/PerconaFT/src/tests/upgrade-test-7.cc
new file mode 100644
index 00000000000..53955cd20e4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/upgrade-test-7.cc
@@ -0,0 +1,144 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+// Purpose of this test is to verify that an environment created by PerconaFT 3.1.0
+// is properly rejected by the upgrade logic of PerconaFT 5.x and later.
+
+#include "test.h"
+#include "toku_pthread.h"
+#include <db.h>
+#include <sys/stat.h>
+#include "ydb-internal.h"
+
+DB_ENV *env;
+
+
+char *free_me = NULL;
+char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+char *tmp_subdir = "tmp.subdir";
+
+#define OLDDATADIR "../../../../tokudb.data/"
+ char *db_v3_dir = OLDDATADIR "env_preload.3.1.0.simple.cleanshutdown";
+
+
+static void setup(void) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+ char * src_db_dir;
+
+ src_db_dir = db_v3_dir;
+
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+}
+
+
+
+
+static void run_test(void)
+{
+ int r;
+
+ setup();
+ {
+ char len = strlen(env_dir) + strlen(tmp_subdir) + 10;
+ char tmpdir[len];
+ r = snprintf(tmpdir, len, "%s/%s", env_dir, tmp_subdir);
+ assert(r<len);
+ r = db_env_create(&env, 0); CKERR(r);
+ //
+ // NOTE: If tmp_dir is set, then attempt to open database created with 3.x will fail with error message:
+ // Couldn't start tokudb because some other tokudb process is using the same directory [dir.upgrade-test-7.c.tdb/tmp.subdir] for [temp]
+ //
+ // r = env->set_tmp_dir(env, tmp_subdir); CKERR(r);
+ //
+ }
+
+ int envflags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_CREATE | DB_PRIVATE;
+ r = env->open(env, env_dir, envflags, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR2(r, TOKUDB_DICTIONARY_TOO_OLD);
+
+ r = env->close(env, 0); CKERR(r);
+}
+
+
+// ------------ infrastructure ----------
+static void do_args(int argc, char * const argv[]);
+
+int test_main(int argc, char * const *argv) {
+ do_args(argc, argv);
+
+ run_test();
+
+ return 0;
+}
+
+static void do_args(int argc, char * const argv[]) {
+
+ int resultcode;
+ char *cmd = argv[0];
+ argc--; argv++;
+
+ while (argc>0) {
+ if (strcmp(argv[0], "-v")==0) {
+ verbose++;
+ } else if (strcmp(argv[0],"-q")==0) {
+ verbose--;
+ if (verbose<0) verbose=0;
+ } else if (strcmp(argv[0], "-h")==0) {
+ resultcode=0;
+ do_usage:
+ fprintf(stderr, "Usage: -h -v -q\n%s\n", cmd);
+ exit(resultcode);
+ } else {
+ fprintf(stderr, "Unknown arg: %s\n", argv[0]);
+ resultcode=1;
+ goto do_usage;
+ }
+ argc--;
+ argv++;
+ }
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/upgrade_simple.cc b/storage/tokudb/PerconaFT/src/tests/upgrade_simple.cc
new file mode 100644
index 00000000000..0fcf40a7bc4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/upgrade_simple.cc
@@ -0,0 +1,160 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+
+/* Purpose of this test is to verify simplest part of upgrade logic.
+ * Start by creating two very simple 4.x environments,
+ * one in each of two states:
+ * - after a clean shutdown
+ * - without a clean shutdown
+ *
+ * The two different environments will be used to exercise upgrade logic
+ * for 5.x.
+ *
+ */
+
+
+#include "test.h"
+#include <db.h>
+
+static DB_ENV *env;
+
+#define FLAGS_NOLOG DB_INIT_LOCK|DB_INIT_MPOOL|DB_CREATE|DB_PRIVATE
+#define FLAGS_LOG FLAGS_NOLOG|DB_INIT_TXN|DB_INIT_LOG
+
+static int mode = S_IRWXU+S_IRWXG+S_IRWXO;
+
+static void test_shutdown(void);
+
+#define OLDDATADIR "../../../../tokudb.data/"
+
+static char *env_dir = TOKU_TEST_FILENAME; // the default env_dir.
+
+static char * dir_v41_clean = OLDDATADIR "env_simple.4.1.1.cleanshutdown";
+static char * dir_v42_clean = OLDDATADIR "env_simple.4.2.0.cleanshutdown";
+static char * dir_v42_dirty = OLDDATADIR "env_simple.4.2.0.dirtyshutdown";
+static char * dir_v41_dirty_multilogfile = OLDDATADIR "env_preload.4.1.1.multilog.dirtyshutdown";
+static char * dir_v42_dirty_multilogfile = OLDDATADIR "env_preload.4.2.0.multilog.dirtyshutdown";
+
+
+static void
+setup (uint32_t flags, bool clean, bool too_old, char * src_db_dir) {
+ int r;
+ int len = 256;
+ char syscmd[len];
+
+ if (env)
+ test_shutdown();
+
+ r = snprintf(syscmd, len, "rm -rf %s", env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r = snprintf(syscmd, len, "cp -r %s %s", src_db_dir, env_dir);
+ assert(r<len);
+ r = system(syscmd);
+ CKERR(r);
+
+ r=db_env_create(&env, 0);
+ CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, flags, mode);
+ if (clean)
+ CKERR(r);
+ else {
+ if (too_old)
+ CKERR2(r, TOKUDB_DICTIONARY_TOO_OLD);
+ else
+ CKERR2(r, TOKUDB_UPGRADE_FAILURE);
+ }
+}
+
+
+
+static void
+test_shutdown(void) {
+ int r;
+ r=env->close(env, 0); CKERR(r);
+ env = NULL;
+}
+
+
+static void
+test_env_startup(void) {
+ uint32_t flags;
+
+ flags = FLAGS_LOG;
+
+ setup(flags, true, false, dir_v42_clean);
+ print_engine_status(env);
+ test_shutdown();
+
+ setup(flags, false, true, dir_v41_clean);
+ print_engine_status(env);
+ test_shutdown();
+
+ setup(flags, false, false, dir_v42_dirty);
+ if (verbose) {
+ printf("\n\nEngine status after aborted env->open() will have some garbage values:\n");
+ }
+ print_engine_status(env);
+ test_shutdown();
+
+ setup(flags, false, true, dir_v41_dirty_multilogfile);
+ if (verbose) {
+ printf("\n\nEngine status after aborted env->open() will have some garbage values:\n");
+ }
+ print_engine_status(env);
+ test_shutdown();
+
+ setup(flags, false, false, dir_v42_dirty_multilogfile);
+ if (verbose) {
+ printf("\n\nEngine status after aborted env->open() will have some garbage values:\n");
+ }
+ print_engine_status(env);
+ test_shutdown();
+}
+
+
+int
+test_main (int argc, char * const argv[]) {
+ parse_args(argc, argv);
+ test_env_startup();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/xa-bigtxn-discard-abort.cc b/storage/tokudb/PerconaFT/src/tests/xa-bigtxn-discard-abort.cc
new file mode 100644
index 00000000000..9f1b904dfa4
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/xa-bigtxn-discard-abort.cc
@@ -0,0 +1,209 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// Verify that a commit of a prepared txn in recovery retains a db created by it.
+// A checkpoint is taken between the db creation and the txn prepare.
+
+// Verify that an abort of a prepared txn in recovery discards the rows that were inserted.
+// A checkpoint is taken after the rows are inserted and before and the txn prepare.
+
+const int test_nrows = 1000000;
+
+static void create_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void populate_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, 0);
+ CKERR(r);
+
+ for (int i = 0; i < test_nrows; i++) {
+ int k = htonl(i);
+ DBT key = { .data = &k, .size = sizeof k }; DBT val = { .data = &i, .size = sizeof i };
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ }
+
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void check_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, 0);
+ CKERR(r);
+
+ DBC *c = nullptr;
+ r = db->cursor(db, txn, &c, 0);
+ CKERR(r);
+
+ DBT key = {}; key.flags = DB_DBT_REALLOC;
+ DBT val = {}; val.flags = DB_DBT_REALLOC;
+ int i;
+ for (i = 0; 1; i++) {
+ r = c->c_get(c, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k, v;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == (int) htonl(i));
+ assert(val.size == sizeof v);
+ memcpy(&v, val.data, val.size);
+ assert(v == i);
+ }
+ assert(i == 0); // no rows found
+ toku_free(key.data);
+ toku_free(val.data);
+
+ r = c->c_close(c);
+ CKERR(r);
+
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void create_prepared_txn(void) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ create_foo(env, txn);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ populate_foo(env, txn);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ TOKU_XA_XID xid = { 0x1234, 8, 9 };
+ for (int i = 0; i < 8+9; i++) {
+ xid.data[i] = i;
+ }
+ r = txn->xa_prepare(txn, &xid, 0);
+ CKERR(r);
+
+ // discard the txn so that we can close the env and run xa recovery later
+ r = txn->discard(txn, 0);
+ CKERR(r);
+
+ r = env->close(env, TOKUFT_DIRTY_SHUTDOWN);
+ CKERR(r);
+}
+
+static void run_xa_recovery(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // get prepared xid
+ long count;
+ TOKU_XA_XID xid;
+ r = env->txn_xa_recover(env, &xid, 1, &count, DB_FIRST);
+ CKERR(r);
+
+ // abort it
+ DB_TXN *txn = nullptr;
+ r = env->get_txn_from_xid(env, &xid, &txn);
+ CKERR(r);
+ r = txn->abort(txn);
+ CKERR(r);
+
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ check_foo(env, txn);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // run the test
+ create_prepared_txn();
+ run_xa_recovery();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/xa-bigtxn-discard-commit.cc b/storage/tokudb/PerconaFT/src/tests/xa-bigtxn-discard-commit.cc
new file mode 100644
index 00000000000..ecbfa18bdf9
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/xa-bigtxn-discard-commit.cc
@@ -0,0 +1,206 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// Verify that a commit of a prepared txn in recovery retains the rows that it inserted.
+// A checkpoint is taken after the rows are inserted and before and the txn prepare.
+
+const int test_nrows = 1000000;
+
+static void create_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void populate_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db = nullptr;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, 0);
+ CKERR(r);
+
+ for (int i = 0; i < test_nrows; i++) {
+ int k = htonl(i);
+ DBT key = { .data = &k, .size = sizeof k }; DBT val = { .data = &i, .size = sizeof i };
+ r = db->put(db, txn, &key, &val, 0);
+ CKERR(r);
+ }
+
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void check_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, 0);
+ CKERR(r);
+
+ DBC *c = nullptr;
+ r = db->cursor(db, txn, &c, 0);
+ CKERR(r);
+
+ DBT key = {}; key.flags = DB_DBT_REALLOC;
+ DBT val = {}; val.flags = DB_DBT_REALLOC;
+ int i;
+ for (i = 0; 1; i++) {
+ r = c->c_get(c, &key, &val, DB_NEXT);
+ if (r != 0)
+ break;
+ int k, v;
+ assert(key.size == sizeof k);
+ memcpy(&k, key.data, key.size);
+ assert(k == (int) htonl(i));
+ assert(val.size == sizeof v);
+ memcpy(&v, val.data, val.size);
+ assert(v == i);
+ }
+ assert(i == test_nrows);
+ toku_free(key.data);
+ toku_free(val.data);
+
+ r = c->c_close(c);
+ CKERR(r);
+
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void create_prepared_txn(void) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ create_foo(env, txn);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ populate_foo(env, txn);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ TOKU_XA_XID xid = { 0x1234, 8, 9 };
+ for (int i = 0; i < 8+9; i++) {
+ xid.data[i] = i;
+ }
+ r = txn->xa_prepare(txn, &xid, 0);
+ CKERR(r);
+
+ // discard the txn so that we can close the env and run xa recovery later
+ r = txn->discard(txn, 0);
+ CKERR(r);
+
+ r = env->close(env, TOKUFT_DIRTY_SHUTDOWN);
+ CKERR(r);
+}
+
+static void run_xa_recovery(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // get prepared xid
+ long count;
+ TOKU_XA_XID xid;
+ r = env->txn_xa_recover(env, &xid, 1, &count, DB_FIRST);
+ CKERR(r);
+
+ // commit it
+ DB_TXN *txn = nullptr;
+ r = env->get_txn_from_xid(env, &xid, &txn);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ check_foo(env, txn);
+
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // run the test
+ create_prepared_txn();
+ run_xa_recovery();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/xa-dirty-commit.cc b/storage/tokudb/PerconaFT/src/tests/xa-dirty-commit.cc
new file mode 100644
index 00000000000..f198202c0b1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/xa-dirty-commit.cc
@@ -0,0 +1,141 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// Verify that a commit of a prepared txn in recovery retains a db that was created by it.
+// The rollback file is dirty when the environment is closed.
+
+static void create_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void check_foo(DB_ENV *env) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, 0, 0);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void create_prepared_txn(void) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ create_foo(env, txn);
+
+ TOKU_XA_XID xid = { 0x1234, 8, 9 };
+ for (int i = 0; i < 8+9; i++) {
+ xid.data[i] = i;
+ }
+ r = txn->xa_prepare(txn, &xid, 0);
+ CKERR(r);
+
+ // discard the txn so that we can close the env and run xa recovery later
+ r = txn->discard(txn, 0);
+ CKERR(r);
+
+ r = env->close(env, TOKUFT_DIRTY_SHUTDOWN);
+ CKERR(r);
+}
+
+static void run_xa_recovery(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // get prepared xid
+ long count;
+ TOKU_XA_XID xid;
+ r = env->txn_xa_recover(env, &xid, 1, &count, DB_FIRST);
+ CKERR(r);
+
+ // commit it
+ DB_TXN *txn = nullptr;
+ r = env->get_txn_from_xid(env, &xid, &txn);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ check_foo(env);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // run the test
+ create_prepared_txn();
+ run_xa_recovery();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/xa-dirty-rollback.cc b/storage/tokudb/PerconaFT/src/tests/xa-dirty-rollback.cc
new file mode 100644
index 00000000000..e23dcb5020d
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/xa-dirty-rollback.cc
@@ -0,0 +1,141 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// Verify that an abort of a prepared txn in recovery deletes a db created by it.
+// The rollback file is dirty when the environment is closed.
+
+static void create_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void check_foo(DB_ENV *env) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, 0, 0);
+ CKERR2(r, ENOENT);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void create_prepared_txn(void) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ create_foo(env, txn);
+
+ TOKU_XA_XID xid = { 0x1234, 8, 9 };
+ for (int i = 0; i < 8+9; i++) {
+ xid.data[i] = i;
+ }
+ r = txn->xa_prepare(txn, &xid, 0);
+ CKERR(r);
+
+ // discard the txn so that we can close the env and run xa recovery later
+ r = txn->discard(txn, 0);
+ CKERR(r);
+
+ r = env->close(env, TOKUFT_DIRTY_SHUTDOWN);
+ CKERR(r);
+}
+
+static void run_xa_recovery(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // get prepared xid
+ long count;
+ TOKU_XA_XID xid;
+ r = env->txn_xa_recover(env, &xid, 1, &count, DB_FIRST);
+ CKERR(r);
+
+ // abort it
+ DB_TXN *txn = nullptr;
+ r = env->get_txn_from_xid(env, &xid, &txn);
+ CKERR(r);
+ r = txn->abort(txn);
+ CKERR(r);
+
+ check_foo(env);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // run the test
+ create_prepared_txn();
+ run_xa_recovery();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/xa-txn-discard-abort.cc b/storage/tokudb/PerconaFT/src/tests/xa-txn-discard-abort.cc
new file mode 100644
index 00000000000..3496ef493a3
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/xa-txn-discard-abort.cc
@@ -0,0 +1,143 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// Verify that an abort of a prepared txn in recovery removes a db created by it.
+// A checkpoint is taken between the db creation and the txn prepare.
+
+static void create_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void check_foo(DB_ENV *env) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, 0, 0);
+ CKERR2(r, ENOENT);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void create_prepared_txn(void) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ create_foo(env, txn);
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ TOKU_XA_XID xid = { 0x1234, 8, 9 };
+ for (int i = 0; i < 8+9; i++) {
+ xid.data[i] = i;
+ }
+ r = txn->xa_prepare(txn, &xid, 0);
+ CKERR(r);
+
+ // discard the txn so that we can close the env and run xa recovery later
+ r = txn->discard(txn, 0);
+ CKERR(r);
+
+ r = env->close(env, TOKUFT_DIRTY_SHUTDOWN);
+ CKERR(r);
+}
+
+static void run_xa_recovery(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // get prepared xid
+ long count;
+ TOKU_XA_XID xid;
+ r = env->txn_xa_recover(env, &xid, 1, &count, DB_FIRST);
+ CKERR(r);
+
+ // abort it
+ DB_TXN *txn = nullptr;
+ r = env->get_txn_from_xid(env, &xid, &txn);
+ CKERR(r);
+ r = txn->abort(txn);
+ CKERR(r);
+
+ check_foo(env);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // run the test
+ create_prepared_txn();
+ run_xa_recovery();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/xa-txn-discard-commit.cc b/storage/tokudb/PerconaFT/src/tests/xa-txn-discard-commit.cc
new file mode 100644
index 00000000000..1d0f63c3b84
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/xa-txn-discard-commit.cc
@@ -0,0 +1,144 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include "test.h"
+
+// Verify that a commit of a prepared txn in recovery retains a db created by it.
+// A checkpoint is taken between the db creation and the txn prepare.
+
+static void create_foo(DB_ENV *env, DB_TXN *txn) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void check_foo(DB_ENV *env) {
+ int r;
+ DB *db;
+ r = db_create(&db, env, 0);
+ CKERR(r);
+ r = db->open(db, nullptr, "foo.db", 0, DB_BTREE, 0, 0);
+ CKERR(r);
+ r = db->close(db, 0);
+ CKERR(r);
+}
+
+static void create_prepared_txn(void) {
+ int r;
+
+ DB_ENV *env = nullptr;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ DB_TXN *txn = nullptr;
+ r = env->txn_begin(env, nullptr, &txn, 0);
+ CKERR(r);
+
+ create_foo(env, txn);
+
+ r = env->txn_checkpoint(env, 0, 0, 0);
+ CKERR(r);
+
+ TOKU_XA_XID xid = { 0x1234, 8, 9 };
+ for (int i = 0; i < 8+9; i++) {
+ xid.data[i] = i;
+ }
+ r = txn->xa_prepare(txn, &xid, 0);
+ CKERR(r);
+
+ // discard the txn so that we can close the env and run xa recovery later
+ r = txn->discard(txn, 0);
+ CKERR(r);
+
+ r = env->close(env, TOKUFT_DIRTY_SHUTDOWN);
+ CKERR(r);
+}
+
+static void run_xa_recovery(void) {
+ int r;
+
+ DB_ENV *env;
+ r = db_env_create(&env, 0);
+ CKERR(r);
+ r = env->open(env, TOKU_TEST_FILENAME,
+ DB_INIT_MPOOL|DB_CREATE|DB_THREAD |DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_PRIVATE | DB_RECOVER,
+ S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // get prepared xid
+ long count;
+ TOKU_XA_XID xid;
+ r = env->txn_xa_recover(env, &xid, 1, &count, DB_FIRST);
+ CKERR(r);
+
+ // commit it
+ DB_TXN *txn = nullptr;
+ r = env->get_txn_from_xid(env, &xid, &txn);
+ CKERR(r);
+ r = txn->commit(txn, 0);
+ CKERR(r);
+
+ check_foo(env);
+
+ r = env->close(env, 0);
+ CKERR(r);
+}
+
+int test_main (int argc, char *const argv[]) {
+ default_parse_args(argc, argv);
+
+ // init the env directory
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ int r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
+ CKERR(r);
+
+ // run the test
+ create_prepared_txn();
+ run_xa_recovery();
+
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/tests/zombie_db.cc b/storage/tokudb/PerconaFT/src/tests/zombie_db.cc
new file mode 100644
index 00000000000..12534887534
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/zombie_db.cc
@@ -0,0 +1,158 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+/* Purpose of this test is to verify correct behavior of
+ * zombie dbs.
+ *
+ * A db is destroyed when it is closed by the user and there are no txns using it.
+ * If a transaction creates a db and then closes, that leaves an open db with
+ * no transaction associated with it. If another transaction then uses the db,
+ * and then closes it, then that leaves a zombie db. The db is closed, but
+ * cannot be destroyed because there is still a transaction associated with it
+ * (not the transaction that created it).
+ *
+ * Outline of this test:
+ *
+ * begin txn_a
+ * create db for new dictionary "foo"
+ * commit txn_a
+ * => leaves open db with no txn
+ * (releases range lock on "foo" dname in directory)
+ *
+ * begin txn_b
+ * insert into db
+ * close db
+ * => leaves zombie db, held open by txn_b
+ *
+ *
+ * create txn_c
+ *
+ * test1:
+ * try to delete dictionary (env->dbremove(foo))
+ * should return DB_LOCK_NOT_GRANTED because txnB is holding range lock on some part of
+ * the dictionary ("foo") referred to by db
+ *
+ * test2:
+ * try to rename dictionary (env->dbrename(foo->bar))
+ * should return DB_LOCK_NOT_GRANTED because txnB is holding range lock on some part of
+ * the dictionary ("foo") referred to by db
+ *
+ */
+
+#include "test.h"
+#include <db.h>
+
+static DB_ENV *env;
+static DB * db;
+
+static void
+setup (void) {
+ int r;
+ toku_os_recursive_delete(TOKU_TEST_FILENAME);
+ r=toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r=db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r=env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+}
+
+
+
+static void
+test_shutdown(void) {
+ int r;
+ r=env->close(env, 0); CKERR(r);
+}
+
+static void
+test_zombie_db(void) {
+ int r;
+ DBT key, val;
+ DB_TXN * txn_b;
+
+ r=env->txn_begin(env, 0, &txn_b, 0); CKERR(r);
+
+ {
+ DB_TXN * txn_a;
+ dbt_init(&key, "key1", 4);
+ dbt_init(&val, "val1", 4);
+
+ r=env->txn_begin(env, 0, &txn_a, 0); CKERR(r);
+ r=db_create(&db, env, 0); CKERR(r);
+ r=db->open(db, txn_a, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU|S_IRWXG|S_IRWXO); CKERR(r);
+ r=db->put(db, txn_a, &key, &val, 0); CKERR(r);
+ r=txn_a->commit(txn_a, 0); CKERR(r);
+ }
+
+ // db is now open with no associated txn
+
+ {
+ dbt_init(&key, "key2", 4);
+ dbt_init(&val, "val2", 4);
+
+ r = db->put(db, txn_b, &key, &val, 0); CKERR(r);
+ r=db->close(db, 0); CKERR(r);
+ }
+
+ // db is now closed, but cannot be destroyed until txn_b closes
+ // db is now a zombie
+
+ {
+ DB_TXN * txn_c;
+
+ r=env->txn_begin(env, 0, &txn_c, 0); CKERR(r);
+ r = env->dbremove(env, txn_c, "foo.db", NULL, 0);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r = env->dbrename(env, txn_c, "foo.db", NULL, "bar.db", 0);
+ CKERR2(r, DB_LOCK_NOTGRANTED);
+ r=txn_c->commit(txn_c, 0); CKERR(r);
+ }
+
+ r=txn_b->commit(txn_b, 0); CKERR(r);
+
+ // db should now be destroyed
+}
+
+int
+test_main (int argc, char *const argv[]) {
+ parse_args(argc, argv);
+ setup();
+ test_zombie_db();
+ test_shutdown();
+ return 0;
+}
diff --git a/storage/tokudb/PerconaFT/src/toku_patent.cc b/storage/tokudb/PerconaFT/src/toku_patent.cc
new file mode 100644
index 00000000000..c2b01c6422c
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/toku_patent.cc
@@ -0,0 +1,66 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+const char *toku_patent_string = "\
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.\n\
+\n\
+ PerconaFT is free software: you can redistribute it and/or modify\n\
+ it under the terms of the GNU General Public License, version 2,\n\
+ as published by the Free Software Foundation.\n\
+\n\
+ PerconaFT is distributed in the hope that it will be useful,\n\
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
+ GNU General Public License for more details.\n\
+\n\
+ You should have received a copy of the GNU General Public License\n\
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.\n\
+\n\
+----------------------------------------\n\
+\n\
+ PerconaFT is free software: you can redistribute it and/or modify\n\
+ it under the terms of the GNU Affero General Public License, version 3,\n\
+ as published by the Free Software Foundation.\n\
+\n\
+ PerconaFT is distributed in the hope that it will be useful,\n\
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
+ GNU Affero General Public License for more details.\n\
+\n\
+ You should have received a copy of the GNU Affero General Public License\n\
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.\n\\";
diff --git a/storage/tokudb/PerconaFT/src/ydb-internal.h b/storage/tokudb/PerconaFT/src/ydb-internal.h
new file mode 100644
index 00000000000..3737a1caf99
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb-internal.h
@@ -0,0 +1,277 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+#include <db.h>
+#include <limits.h>
+
+#include <ft/cachetable/cachetable.h>
+#include <ft/cursor.h>
+#include <ft/comparator.h>
+#include <ft/logger/logger.h>
+#include <ft/txn/txn.h>
+
+#include <util/growable_array.h>
+#include <util/minicron.h>
+#include <util/omt.h>
+
+#include <locktree/locktree.h>
+#include <locktree/range_buffer.h>
+
+#include <toku_list.h>
+
+struct __toku_db_internal {
+ int opened;
+ uint32_t open_flags;
+ int open_mode;
+ FT_HANDLE ft_handle;
+ DICTIONARY_ID dict_id; // unique identifier used by locktree logic
+ toku::locktree *lt;
+ struct simple_dbt skey, sval; // static key and value
+ bool key_compare_was_set; // true if a comparison function was provided before call to db->open() (if false, use environment's comparison function).
+ char *dname; // dname is constant for this handle (handle must be closed before file is renamed)
+ DB_INDEXER *indexer;
+};
+
+int toku_db_set_indexer(DB *db, DB_INDEXER *indexer);
+DB_INDEXER *toku_db_get_indexer(DB *db);
+
+#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 1
+typedef void (*toku_env_errcall_t)(const char *, char *);
+#elif DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
+typedef void (*toku_env_errcall_t)(const DB_ENV *, const char *, const char *);
+#else
+#error
+#endif
+
+struct __toku_db_env_internal {
+ int is_panicked; // if nonzero, then its an error number
+ char *panic_string;
+ uint32_t open_flags;
+ int open_mode;
+ toku_env_errcall_t errcall;
+ void *errfile;
+ const char *errpfx;
+ char *dir; /* A malloc'd copy of the directory. */
+ char *tmp_dir;
+ char *lg_dir;
+ char *data_dir;
+ int (*bt_compare) (DB *, const DBT *, const DBT *);
+ int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra);
+ generate_row_for_put_func generate_row_for_put;
+ generate_row_for_del_func generate_row_for_del;
+
+ unsigned long cachetable_size;
+ unsigned long client_pool_threads;
+ unsigned long cachetable_pool_threads;
+ unsigned long checkpoint_pool_threads;
+ CACHETABLE cachetable;
+ TOKULOGGER logger;
+ toku::locktree_manager ltm;
+ lock_timeout_callback lock_wait_timeout_callback; // Called when a lock request times out waiting for a lock.
+
+ DB *directory; // Maps dnames to inames
+ DB *persistent_environment; // Stores environment settings, can be used for upgrade
+ toku::omt<DB *> *open_dbs_by_dname; // Stores open db handles, sorted first by dname and then by numerical value of pointer to the db (arbitrarily assigned memory location)
+ toku::omt<DB *> *open_dbs_by_dict_id; // Stores open db handles, sorted by dictionary id and then by numerical value of pointer to the db (arbitrarily assigned memory location)
+ toku_pthread_rwlock_t open_dbs_rwlock; // rwlock that protects the OMT of open dbs.
+
+ char *real_data_dir; // data dir used when the env is opened (relative to cwd, or absolute with leading /)
+ char *real_log_dir; // log dir used when the env is opened (relative to cwd, or absolute with leading /)
+ char *real_tmp_dir; // tmp dir used for temporary files (relative to cwd, or absoulte with leading /)
+
+ fs_redzone_state fs_state;
+ uint64_t fs_seq; // how many times has fs_poller run?
+ uint64_t last_seq_entered_red;
+ uint64_t last_seq_entered_yellow;
+ int redzone; // percent of total fs space that marks boundary between yellow and red zones
+ int enospc_redzone_ctr; // number of operations rejected by enospc prevention (red zone)
+ int fs_poll_time; // Time in seconds between statfs calls
+ struct minicron fs_poller; // Poll the file systems
+ bool fs_poller_is_init;
+ uint32_t fsync_log_period_ms;
+ bool fsync_log_cron_is_init;
+ struct minicron fsync_log_cron; // fsync recovery log
+ int envdir_lockfd;
+ int datadir_lockfd;
+ int logdir_lockfd;
+ int tmpdir_lockfd;
+ uint64_t (*get_loader_memory_size_callback)(void);
+ uint64_t default_lock_timeout_msec;
+ uint64_t (*get_lock_timeout_callback)(uint64_t default_lock_timeout_msec);
+ uint64_t default_killed_time_msec;
+ uint64_t (*get_killed_time_callback)(uint64_t default_killed_time_msec);
+ int (*killed_callback)(void);
+};
+
+// test-only environment function for running lock escalation
+static inline void toku_env_run_lock_escalation_for_test(DB_ENV *env) {
+ toku::locktree_manager *mgr = &env->i->ltm;
+ mgr->run_escalation_for_test();
+}
+
+// Common error handling macros and panic detection
+#define MAYBE_RETURN_ERROR(cond, status) if (cond) return status;
+#define HANDLE_PANICKED_ENV(env) if (toku_env_is_panicked(env)) { sleep(1); return EINVAL; }
+#define HANDLE_PANICKED_DB(db) HANDLE_PANICKED_ENV(db->dbenv)
+
+// Only commit/abort/prelock (which are used by handlerton) are allowed when a child exists.
+#define HANDLE_ILLEGAL_WORKING_PARENT_TXN(env, txn) \
+ MAYBE_RETURN_ERROR(((txn) && db_txn_struct_i(txn)->child), \
+ toku_ydb_do_error((env), \
+ EINVAL, \
+ "%s: Transaction cannot do work when child exists\n", __FUNCTION__))
+
+#define HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn) \
+ HANDLE_ILLEGAL_WORKING_PARENT_TXN((db)->dbenv, txn)
+
+#define HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c) \
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN((c)->dbp, dbc_struct_i(c)->txn)
+
+// Bail out if we get unknown flags
+#define HANDLE_EXTRA_FLAGS(env, flags_to_function, allowed_flags) \
+ MAYBE_RETURN_ERROR((env) && ((flags_to_function) & ~(allowed_flags)), \
+ toku_ydb_do_error((env), \
+ EINVAL, \
+ "Unknown flags (%" PRIu32 ") in " __FILE__ ":%s(): %d\n", (flags_to_function) & ~(allowed_flags), __FUNCTION__, __LINE__))
+
+int toku_ydb_check_avail_fs_space(DB_ENV *env);
+
+void toku_ydb_error_all_cases(const DB_ENV * env,
+ int error,
+ bool include_stderrstring,
+ bool use_stderr_if_nothing_else,
+ const char *fmt, va_list ap)
+ __attribute__((format (printf, 5, 0)))
+ __attribute__((__visibility__("default"))); // this is needed by the C++ interface.
+
+int toku_ydb_do_error (const DB_ENV *dbenv, int error, const char *string, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+
+/* Environment related errors */
+int toku_env_is_panicked(DB_ENV *dbenv);
+void toku_env_err(const DB_ENV * env, int error, const char *fmt, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+
+typedef enum __toku_isolation_level {
+ TOKU_ISO_SERIALIZABLE=0,
+ TOKU_ISO_SNAPSHOT=1,
+ TOKU_ISO_READ_COMMITTED=2,
+ TOKU_ISO_READ_UNCOMMITTED=3,
+ TOKU_ISO_READ_COMMITTED_ALWAYS=4
+} TOKU_ISOLATION;
+
+// needed in ydb_db.c
+#define DB_ISOLATION_FLAGS (DB_READ_COMMITTED | DB_READ_COMMITTED_ALWAYS | DB_READ_UNCOMMITTED | DB_TXN_SNAPSHOT | DB_SERIALIZABLE | DB_INHERIT_ISOLATION)
+
+struct txn_lock_range {
+ DBT left;
+ DBT right;
+};
+
+struct txn_lt_key_ranges {
+ toku::locktree *lt;
+ toku::range_buffer *buffer;
+};
+
+struct __toku_db_txn_internal {
+ struct tokutxn *tokutxn;
+ uint32_t flags;
+ TOKU_ISOLATION iso;
+ DB_TXN *child;
+ toku_mutex_t txn_mutex;
+
+ // maps a locktree to a buffer of key ranges that are locked.
+ // it is protected by the txn_mutex, so hot indexing and a client
+ // thread can concurrently operate on this txn.
+ toku::omt<txn_lt_key_ranges> lt_map;
+};
+
+struct __toku_db_txn_external {
+ struct __toku_db_txn external_part;
+ struct __toku_db_txn_internal internal_part;
+};
+#define db_txn_struct_i(x) (&((struct __toku_db_txn_external *)x)->internal_part)
+
+struct __toku_dbc_internal {
+ struct ft_cursor ftcursor;
+ DB_TXN *txn;
+ TOKU_ISOLATION iso;
+ struct simple_dbt skey_s,sval_s;
+ struct simple_dbt *skey,*sval;
+
+ // if the rmw flag is asserted, cursor operations (like set) grab write locks instead of read locks
+ // the rmw flag is set when the cursor is created with the DB_RMW flag set
+ bool rmw;
+};
+
+static_assert(sizeof(__toku_dbc_internal) <= sizeof(((DBC *) nullptr)->_internal),
+ "__toku_dbc_internal doesn't fit in the internal portion of a DBC");
+
+static inline __toku_dbc_internal *dbc_struct_i(DBC *c) {
+ union dbc_union {
+ __toku_dbc_internal *dbc_internal;
+ char *buf;
+ } u;
+ u.buf = c->_internal;
+ return u.dbc_internal;
+}
+
+static inline struct ft_cursor *dbc_ftcursor(DBC *c) {
+ return &dbc_struct_i(c)->ftcursor;
+}
+
+static inline int
+env_opened(DB_ENV *env) {
+ return env->i->cachetable != 0;
+}
+
+static inline bool
+txn_is_read_only(DB_TXN* txn) {
+ if (txn && (db_txn_struct_i(txn)->flags & DB_TXN_READ_ONLY)) {
+ return true;
+ }
+ return false;
+}
+
+#define HANDLE_READ_ONLY_TXN(txn) if(txn_is_read_only(txn)) return EINVAL;
+
+void env_panic(DB_ENV * env, int cause, const char * msg);
+void env_note_db_opened(DB_ENV *env, DB *db);
+void env_note_db_closed(DB_ENV *env, DB *db);
diff --git a/storage/tokudb/PerconaFT/src/ydb.cc b/storage/tokudb/PerconaFT/src/ydb.cc
new file mode 100644
index 00000000000..88c6c86f214
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb.cc
@@ -0,0 +1,3164 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+extern const char *toku_patent_string;
+const char *toku_copyright_string = "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.";
+
+#include <db.h>
+#include <errno.h>
+#include <string.h>
+
+#include "portability/memory.h"
+#include "portability/toku_assert.h"
+#include "portability/toku_portability.h"
+#include "portability/toku_pthread.h"
+#include "portability/toku_stdlib.h"
+
+#include "ft/ft-flusher.h"
+#include "ft/cachetable/cachetable.h"
+#include "ft/cachetable/checkpoint.h"
+#include "ft/logger/log.h"
+#include "ft/loader/loader.h"
+#include "ft/log_header.h"
+#include "ft/ft.h"
+#include "ft/txn/txn_manager.h"
+#include "src/ydb.h"
+#include "src/ydb-internal.h"
+#include "src/ydb_cursor.h"
+#include "src/ydb_row_lock.h"
+#include "src/ydb_env_func.h"
+#include "src/ydb_db.h"
+#include "src/ydb_write.h"
+#include "src/ydb_txn.h"
+#include "src/loader.h"
+#include "src/indexer.h"
+#include "util/status.h"
+#include "util/context.h"
+
+// Include ydb_lib.cc here so that its constructor/destructor gets put into
+// ydb.o, to make sure they don't get erased at link time (when linking to
+// a static libtokufractaltree.a that was compiled with gcc). See #5094.
+#include "ydb_lib.cc"
+
+#ifdef TOKUTRACE
+ #define DB_ENV_CREATE_FUN db_env_create_toku10
+ #define DB_CREATE_FUN db_create_toku10
+#else
+ #define DB_ENV_CREATE_FUN db_env_create
+ #define DB_CREATE_FUN db_create
+ int toku_set_trace_file (const char *fname __attribute__((__unused__))) { return 0; }
+ int toku_close_trace_file (void) { return 0; }
+#endif
+
+// Set when env is panicked, never cleared.
+static int env_is_panicked = 0;
+
+void
+env_panic(DB_ENV * env, int cause, const char * msg) {
+ if (cause == 0)
+ cause = -1; // if unknown cause, at least guarantee panic
+ if (msg == NULL)
+ msg = "Unknown cause in env_panic\n";
+ env_is_panicked = cause;
+ env->i->is_panicked = cause;
+ env->i->panic_string = toku_strdup(msg);
+}
+
+static int env_get_engine_status_num_rows (DB_ENV * UU(env), uint64_t * num_rowsp);
+
+/********************************************************************************
+ * Status is intended for display to humans to help understand system behavior.
+ * It does not need to be perfectly thread-safe.
+ */
+
+typedef enum {
+ YDB_LAYER_TIME_CREATION = 0, /* timestamp of environment creation, read from persistent environment */
+ YDB_LAYER_TIME_STARTUP, /* timestamp of system startup */
+ YDB_LAYER_TIME_NOW, /* timestamp of engine status query */
+ YDB_LAYER_NUM_DB_OPEN,
+ YDB_LAYER_NUM_DB_CLOSE,
+ YDB_LAYER_NUM_OPEN_DBS,
+ YDB_LAYER_MAX_OPEN_DBS,
+ YDB_LAYER_FSYNC_LOG_PERIOD,
+#if 0
+ YDB_LAYER_ORIGINAL_ENV_VERSION, /* version of original environment, read from persistent environment */
+ YDB_LAYER_STARTUP_ENV_VERSION, /* version of environment at this startup, read from persistent environment (curr_env_ver_key) */
+ YDB_LAYER_LAST_LSN_OF_V13, /* read from persistent environment */
+ YDB_LAYER_UPGRADE_V14_TIME, /* timestamp of upgrade to version 14, read from persistent environment */
+ YDB_LAYER_UPGRADE_V14_FOOTPRINT, /* footprint of upgrade to version 14, read from persistent environment */
+#endif
+ YDB_LAYER_STATUS_NUM_ROWS /* number of rows in this status array */
+} ydb_layer_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[YDB_LAYER_STATUS_NUM_ROWS];
+} YDB_LAYER_STATUS_S, *YDB_LAYER_STATUS;
+
+static YDB_LAYER_STATUS_S ydb_layer_status;
+#define STATUS_VALUE(x) ydb_layer_status.status[x].value.num
+
+#define STATUS_INIT(k,c,t,l,inc) TOKUFT_STATUS_INIT(ydb_layer_status, k, c, t, l, inc)
+
+static void
+ydb_layer_status_init (void) {
+ // Note, this function initializes the keyname, type, and legend fields.
+ // Value fields are initialized to zero by compiler.
+
+ STATUS_INIT(YDB_LAYER_TIME_CREATION, nullptr, UNIXTIME, "time of environment creation", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_TIME_STARTUP, nullptr, UNIXTIME, "time of engine startup", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_TIME_NOW, nullptr, UNIXTIME, "time now", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_DB_OPEN, DB_OPENS, UINT64, "db opens", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_DB_CLOSE, DB_CLOSES, UINT64, "db closes", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_OPEN_DBS, DB_OPEN_CURRENT, UINT64, "num open dbs now", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ STATUS_INIT(YDB_LAYER_MAX_OPEN_DBS, DB_OPEN_MAX, UINT64, "max open dbs", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ STATUS_INIT(YDB_LAYER_FSYNC_LOG_PERIOD, nullptr, UINT64, "period, in ms, that recovery log is automatically fsynced", TOKU_ENGINE_STATUS);
+
+ STATUS_VALUE(YDB_LAYER_TIME_STARTUP) = time(NULL);
+ ydb_layer_status.initialized = true;
+}
+#undef STATUS_INIT
+
+static void
+ydb_layer_get_status(DB_ENV* env, YDB_LAYER_STATUS statp) {
+ STATUS_VALUE(YDB_LAYER_TIME_NOW) = time(NULL);
+ STATUS_VALUE(YDB_LAYER_FSYNC_LOG_PERIOD) = toku_minicron_get_period_in_ms_unlocked(&env->i->fsync_log_cron);
+ *statp = ydb_layer_status;
+}
+
+/********************************************************************************
+ * End of ydb_layer local status section.
+ */
+
+static DB_ENV * volatile most_recent_env; // most recently opened env, used for engine status on crash. Note there are likely to be races on this if you have multiple threads creating and closing environments in parallel. We'll declare it volatile since at least that helps make sure the compiler doesn't optimize away certain code (e.g., if while debugging, you write a code that spins on most_recent_env, you'd like to compiler not to optimize your code away.)
+
+static int env_get_iname(DB_ENV* env, DBT* dname_dbt, DBT* iname_dbt);
+static int toku_maybe_get_engine_status_text (char* buff, int buffsize); // for use by toku_assert
+static int toku_maybe_err_engine_status (void);
+static void toku_maybe_set_env_panic(int code, const char * msg); // for use by toku_assert
+
+int
+toku_ydb_init(void) {
+ int r = 0;
+ //Lower level must be initialized first.
+ r = toku_ft_layer_init();
+ return r;
+}
+
+// Do not clean up resources if env is panicked, just exit ugly
+void
+toku_ydb_destroy(void) {
+ if (env_is_panicked == 0) {
+ toku_ft_layer_destroy();
+ }
+}
+
+static int
+ydb_getf_do_nothing(DBT const* UU(key), DBT const* UU(val), void* UU(extra)) {
+ return 0;
+}
+
+/* env methods */
+
+static void
+env_fs_report_in_yellow(DB_ENV *UU(env)) {
+ char tbuf[26];
+ time_t tnow = time(NULL);
+ fprintf(stderr, "%.24s PerconaFT file system space is low\n", ctime_r(&tnow, tbuf)); fflush(stderr);
+}
+
+static void
+env_fs_report_in_red(DB_ENV *UU(env)) {
+ char tbuf[26];
+ time_t tnow = time(NULL);
+ fprintf(stderr, "%.24s PerconaFT file system space is really low and access is restricted\n", ctime_r(&tnow, tbuf)); fflush(stderr);
+}
+
+static inline uint64_t
+env_fs_redzone(DB_ENV *env, uint64_t total) {
+ return total * env->i->redzone / 100;
+}
+
+#define ZONEREPORTLIMIT 12
+// Check the available space in the file systems used by tokuft and erect barriers when available space gets low.
+static int
+env_fs_poller(void *arg) {
+ DB_ENV *env = (DB_ENV *) arg;
+ int r;
+
+ int in_yellow; // set true to issue warning to user
+ int in_red; // set true to prevent certain operations (returning ENOSPC)
+
+ // get the fs sizes for the home dir
+ uint64_t avail_size, total_size;
+ r = toku_get_filesystem_sizes(env->i->dir, &avail_size, NULL, &total_size);
+ assert(r == 0);
+ in_yellow = (avail_size < 2 * env_fs_redzone(env, total_size));
+ in_red = (avail_size < env_fs_redzone(env, total_size));
+
+ // get the fs sizes for the data dir if different than the home dir
+ if (strcmp(env->i->dir, env->i->real_data_dir) != 0) {
+ r = toku_get_filesystem_sizes(env->i->real_data_dir, &avail_size, NULL, &total_size);
+ assert(r == 0);
+ in_yellow += (avail_size < 2 * env_fs_redzone(env, total_size));
+ in_red += (avail_size < env_fs_redzone(env, total_size));
+ }
+
+ // get the fs sizes for the log dir if different than the home dir and data dir
+ if (strcmp(env->i->dir, env->i->real_log_dir) != 0 && strcmp(env->i->real_data_dir, env->i->real_log_dir) != 0) {
+ r = toku_get_filesystem_sizes(env->i->real_log_dir, &avail_size, NULL, &total_size);
+ assert(r == 0);
+ in_yellow += (avail_size < 2 * env_fs_redzone(env, total_size));
+ in_red += (avail_size < env_fs_redzone(env, total_size));
+ }
+
+ env->i->fs_seq++; // how many times through this polling loop?
+ uint64_t now = env->i->fs_seq;
+
+ // Don't issue report if we have not been out of this fs_state for a while, unless we're at system startup
+ switch (env->i->fs_state) {
+ case FS_RED:
+ if (!in_red) {
+ if (in_yellow) {
+ env->i->fs_state = FS_YELLOW;
+ } else {
+ env->i->fs_state = FS_GREEN;
+ }
+ }
+ break;
+ case FS_YELLOW:
+ if (in_red) {
+ if ((now - env->i->last_seq_entered_red > ZONEREPORTLIMIT) || (now < ZONEREPORTLIMIT))
+ env_fs_report_in_red(env);
+ env->i->fs_state = FS_RED;
+ env->i->last_seq_entered_red = now;
+ } else if (!in_yellow) {
+ env->i->fs_state = FS_GREEN;
+ }
+ break;
+ case FS_GREEN:
+ if (in_red) {
+ if ((now - env->i->last_seq_entered_red > ZONEREPORTLIMIT) || (now < ZONEREPORTLIMIT))
+ env_fs_report_in_red(env);
+ env->i->fs_state = FS_RED;
+ env->i->last_seq_entered_red = now;
+ } else if (in_yellow) {
+ if ((now - env->i->last_seq_entered_yellow > ZONEREPORTLIMIT) || (now < ZONEREPORTLIMIT))
+ env_fs_report_in_yellow(env);
+ env->i->fs_state = FS_YELLOW;
+ env->i->last_seq_entered_yellow = now;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ return 0;
+}
+#undef ZONEREPORTLIMIT
+
+static void
+env_fs_init(DB_ENV *env) {
+ env->i->fs_state = FS_GREEN;
+ env->i->fs_poll_time = 5; // seconds
+ env->i->redzone = 5; // percent of total space
+ env->i->fs_poller_is_init = false;
+}
+
+// Initialize the minicron that polls file system space
+static int
+env_fs_init_minicron(DB_ENV *env) {
+ int r = toku_minicron_setup(&env->i->fs_poller, env->i->fs_poll_time*1000, env_fs_poller, env);
+ if (r == 0)
+ env->i->fs_poller_is_init = true;
+ return r;
+}
+
+// Destroy the file system space minicron
+static void
+env_fs_destroy(DB_ENV *env) {
+ if (env->i->fs_poller_is_init) {
+ int r = toku_minicron_shutdown(&env->i->fs_poller);
+ assert(r == 0);
+ env->i->fs_poller_is_init = false;
+ }
+}
+
+static int
+env_fsync_log_on_minicron(void *arg) {
+ DB_ENV *env = (DB_ENV *) arg;
+ int r = env->log_flush(env, 0);
+ assert(r == 0);
+ return 0;
+}
+
+static void
+env_fsync_log_init(DB_ENV *env) {
+ env->i->fsync_log_period_ms = 0;
+ env->i->fsync_log_cron_is_init = false;
+}
+
+static void UU()
+env_change_fsync_log_period(DB_ENV* env, uint32_t period_ms) {
+ env->i->fsync_log_period_ms = period_ms;
+ if (env->i->fsync_log_cron_is_init) {
+ toku_minicron_change_period(&env->i->fsync_log_cron, period_ms);
+ }
+}
+
+static int
+env_fsync_log_cron_init(DB_ENV *env) {
+ int r = toku_minicron_setup(&env->i->fsync_log_cron, env->i->fsync_log_period_ms, env_fsync_log_on_minicron, env);
+ if (r == 0)
+ env->i->fsync_log_cron_is_init = true;
+ return r;
+}
+
+static void
+env_fsync_log_cron_destroy(DB_ENV *env) {
+ if (env->i->fsync_log_cron_is_init) {
+ int r = toku_minicron_shutdown(&env->i->fsync_log_cron);
+ assert(r == 0);
+ env->i->fsync_log_cron_is_init = false;
+ }
+}
+
+static void
+env_setup_real_dir(DB_ENV *env, char **real_dir, const char *nominal_dir) {
+ toku_free(*real_dir);
+ *real_dir = NULL;
+
+ assert(env->i->dir);
+ if (nominal_dir)
+ *real_dir = toku_construct_full_name(2, env->i->dir, nominal_dir);
+ else
+ *real_dir = toku_strdup(env->i->dir);
+}
+
+static void
+env_setup_real_data_dir(DB_ENV *env) {
+ env_setup_real_dir(env, &env->i->real_data_dir, env->i->data_dir);
+}
+
+static void
+env_setup_real_log_dir(DB_ENV *env) {
+ env_setup_real_dir(env, &env->i->real_log_dir, env->i->lg_dir);
+}
+
+static void
+env_setup_real_tmp_dir(DB_ENV *env) {
+ env_setup_real_dir(env, &env->i->real_tmp_dir, env->i->tmp_dir);
+}
+
+static void keep_cachetable_callback (DB_ENV *env, CACHETABLE cachetable)
+{
+ env->i->cachetable = cachetable;
+}
+
+static int
+ydb_do_recovery (DB_ENV *env) {
+ assert(env->i->real_log_dir);
+ int r = tokuft_recover(env,
+ toku_keep_prepared_txn_callback,
+ keep_cachetable_callback,
+ env->i->logger,
+ env->i->dir, env->i->real_log_dir, env->i->bt_compare,
+ env->i->update_function,
+ env->i->generate_row_for_put, env->i->generate_row_for_del,
+ env->i->cachetable_size);
+ return r;
+}
+
+static int
+needs_recovery (DB_ENV *env) {
+ assert(env->i->real_log_dir);
+ int recovery_needed = tokuft_needs_recovery(env->i->real_log_dir, true);
+ return recovery_needed ? DB_RUNRECOVERY : 0;
+}
+
+static int toku_env_txn_checkpoint(DB_ENV * env, uint32_t kbyte, uint32_t min, uint32_t flags);
+
+// Keys used in persistent environment dictionary:
+// Following keys added in version 12
+static const char * orig_env_ver_key = "original_version";
+static const char * curr_env_ver_key = "current_version";
+// Following keys added in version 14, add more keys for future versions
+static const char * creation_time_key = "creation_time";
+
+static char * get_upgrade_time_key(int version) {
+ static char upgrade_time_key[sizeof("upgrade_v_time") + 12];
+ {
+ int n;
+ n = snprintf(upgrade_time_key, sizeof(upgrade_time_key), "upgrade_v%d_time", version);
+ assert(n >= 0 && n < (int)sizeof(upgrade_time_key));
+ }
+ return &upgrade_time_key[0];
+}
+
+static char * get_upgrade_footprint_key(int version) {
+ static char upgrade_footprint_key[sizeof("upgrade_v_footprint") + 12];
+ {
+ int n;
+ n = snprintf(upgrade_footprint_key, sizeof(upgrade_footprint_key), "upgrade_v%d_footprint", version);
+ assert(n >= 0 && n < (int)sizeof(upgrade_footprint_key));
+ }
+ return &upgrade_footprint_key[0];
+}
+
+static char * get_upgrade_last_lsn_key(int version) {
+ static char upgrade_last_lsn_key[sizeof("upgrade_v_last_lsn") + 12];
+ {
+ int n;
+ n = snprintf(upgrade_last_lsn_key, sizeof(upgrade_last_lsn_key), "upgrade_v%d_last_lsn", version);
+ assert(n >= 0 && n < (int)sizeof(upgrade_last_lsn_key));
+ }
+ return &upgrade_last_lsn_key[0];
+}
+
+// Values read from (or written into) persistent environment,
+// kept here for read-only access from engine status.
+// Note, persistent_upgrade_status info is separate in part to simplify its exclusion from engine status until relevant.
+typedef enum {
+ PERSISTENT_UPGRADE_ORIGINAL_ENV_VERSION = 0,
+ PERSISTENT_UPGRADE_STORED_ENV_VERSION_AT_STARTUP, // read from curr_env_ver_key, prev version as of this startup
+ PERSISTENT_UPGRADE_LAST_LSN_OF_V13,
+ PERSISTENT_UPGRADE_V14_TIME,
+ PERSISTENT_UPGRADE_V14_FOOTPRINT,
+ PERSISTENT_UPGRADE_STATUS_NUM_ROWS
+} persistent_upgrade_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[PERSISTENT_UPGRADE_STATUS_NUM_ROWS];
+} PERSISTENT_UPGRADE_STATUS_S, *PERSISTENT_UPGRADE_STATUS;
+
+static PERSISTENT_UPGRADE_STATUS_S persistent_upgrade_status;
+
+#define PERSISTENT_UPGRADE_STATUS_INIT(k,c,t,l,inc) TOKUFT_STATUS_INIT(persistent_upgrade_status, k, c, t, "upgrade: " l, inc)
+
+static void
+persistent_upgrade_status_init (void) {
+ // Note, this function initializes the keyname, type, and legend fields.
+ // Value fields are initialized to zero by compiler.
+
+ PERSISTENT_UPGRADE_STATUS_INIT(PERSISTENT_UPGRADE_ORIGINAL_ENV_VERSION, nullptr, UINT64, "original version (at time of environment creation)", TOKU_ENGINE_STATUS);
+ PERSISTENT_UPGRADE_STATUS_INIT(PERSISTENT_UPGRADE_STORED_ENV_VERSION_AT_STARTUP, nullptr, UINT64, "version at time of startup", TOKU_ENGINE_STATUS);
+ PERSISTENT_UPGRADE_STATUS_INIT(PERSISTENT_UPGRADE_LAST_LSN_OF_V13, nullptr, UINT64, "last LSN of version 13", TOKU_ENGINE_STATUS);
+ PERSISTENT_UPGRADE_STATUS_INIT(PERSISTENT_UPGRADE_V14_TIME, nullptr, UNIXTIME, "time of upgrade to version 14", TOKU_ENGINE_STATUS);
+ PERSISTENT_UPGRADE_STATUS_INIT(PERSISTENT_UPGRADE_V14_FOOTPRINT, nullptr, UINT64, "footprint from version 13 to 14", TOKU_ENGINE_STATUS);
+ persistent_upgrade_status.initialized = true;
+}
+
+#define PERSISTENT_UPGRADE_STATUS_VALUE(x) persistent_upgrade_status.status[x].value.num
+
+// Requires: persistent environment dictionary is already open.
+// Input arg is lsn of clean shutdown of previous version,
+// or ZERO_LSN if no upgrade or if crash between log upgrade and here.
+// NOTE: To maintain compatibility with previous versions, do not change the
+// format of any information stored in the persistent environment dictionary.
+// For example, some values are stored as 32 bits, even though they are immediately
+// converted to 64 bits when read. Do not change them to be stored as 64 bits.
+//
+static int
+maybe_upgrade_persistent_environment_dictionary(DB_ENV * env, DB_TXN * txn, LSN last_lsn_of_clean_shutdown_read_from_log) {
+ int r;
+ DBT key, val;
+ DB *persistent_environment = env->i->persistent_environment;
+
+ if (!persistent_upgrade_status.initialized)
+ persistent_upgrade_status_init();
+
+ toku_fill_dbt(&key, curr_env_ver_key, strlen(curr_env_ver_key));
+ toku_init_dbt(&val);
+ r = toku_db_get(persistent_environment, txn, &key, &val, 0);
+ assert(r == 0);
+ uint32_t stored_env_version = toku_dtoh32(*(uint32_t*)val.data);
+ PERSISTENT_UPGRADE_STATUS_VALUE(PERSISTENT_UPGRADE_STORED_ENV_VERSION_AT_STARTUP) = stored_env_version;
+ if (stored_env_version > FT_LAYOUT_VERSION)
+ r = TOKUDB_DICTIONARY_TOO_NEW;
+ else if (stored_env_version < FT_LAYOUT_MIN_SUPPORTED_VERSION)
+ r = TOKUDB_DICTIONARY_TOO_OLD;
+ else if (stored_env_version < FT_LAYOUT_VERSION) {
+ const uint32_t curr_env_ver_d = toku_htod32(FT_LAYOUT_VERSION);
+ toku_fill_dbt(&key, curr_env_ver_key, strlen(curr_env_ver_key));
+ toku_fill_dbt(&val, &curr_env_ver_d, sizeof(curr_env_ver_d));
+ r = toku_db_put(persistent_environment, txn, &key, &val, 0, false);
+ assert_zero(r);
+
+ time_t upgrade_time_d = toku_htod64(time(NULL));
+ uint64_t upgrade_footprint_d = toku_htod64(toku_log_upgrade_get_footprint());
+ uint64_t upgrade_last_lsn_d = toku_htod64(last_lsn_of_clean_shutdown_read_from_log.lsn);
+ for (int version = stored_env_version+1; version <= FT_LAYOUT_VERSION; version++) {
+ uint32_t put_flag = DB_NOOVERWRITE;
+ if (version <= FT_LAYOUT_VERSION_19) {
+ // See #5902.
+ // To prevent a crash (and any higher complexity code) we'll simply
+ // silently not overwrite anything if it exists.
+ // The keys existing for version <= 19 is not necessarily an error.
+ // If this happens for versions > 19 it IS an error and we'll use DB_NOOVERWRITE.
+ put_flag = DB_NOOVERWRITE_NO_ERROR;
+ }
+
+
+ char* upgrade_time_key = get_upgrade_time_key(version);
+ toku_fill_dbt(&key, upgrade_time_key, strlen(upgrade_time_key));
+ toku_fill_dbt(&val, &upgrade_time_d, sizeof(upgrade_time_d));
+ r = toku_db_put(persistent_environment, txn, &key, &val, put_flag, false);
+ assert_zero(r);
+
+ char* upgrade_footprint_key = get_upgrade_footprint_key(version);
+ toku_fill_dbt(&key, upgrade_footprint_key, strlen(upgrade_footprint_key));
+ toku_fill_dbt(&val, &upgrade_footprint_d, sizeof(upgrade_footprint_d));
+ r = toku_db_put(persistent_environment, txn, &key, &val, put_flag, false);
+ assert_zero(r);
+
+ char* upgrade_last_lsn_key = get_upgrade_last_lsn_key(version);
+ toku_fill_dbt(&key, upgrade_last_lsn_key, strlen(upgrade_last_lsn_key));
+ toku_fill_dbt(&val, &upgrade_last_lsn_d, sizeof(upgrade_last_lsn_d));
+ r = toku_db_put(persistent_environment, txn, &key, &val, put_flag, false);
+ assert_zero(r);
+ }
+
+ }
+ return r;
+}
+
+// Capture contents of persistent_environment dictionary so that it can be read by engine status
+static void
+capture_persistent_env_contents (DB_ENV * env, DB_TXN * txn) {
+ int r;
+ DBT key, val;
+ DB *persistent_environment = env->i->persistent_environment;
+
+ toku_fill_dbt(&key, curr_env_ver_key, strlen(curr_env_ver_key));
+ toku_init_dbt(&val);
+ r = toku_db_get(persistent_environment, txn, &key, &val, 0);
+ assert_zero(r);
+ uint32_t curr_env_version = toku_dtoh32(*(uint32_t*)val.data);
+ assert(curr_env_version == FT_LAYOUT_VERSION);
+
+ toku_fill_dbt(&key, orig_env_ver_key, strlen(orig_env_ver_key));
+ toku_init_dbt(&val);
+ r = toku_db_get(persistent_environment, txn, &key, &val, 0);
+ assert_zero(r);
+ uint64_t persistent_original_env_version = toku_dtoh32(*(uint32_t*)val.data);
+ PERSISTENT_UPGRADE_STATUS_VALUE(PERSISTENT_UPGRADE_ORIGINAL_ENV_VERSION) = persistent_original_env_version;
+ assert(persistent_original_env_version <= curr_env_version);
+
+ // make no assertions about timestamps, clock may have been reset
+ if (persistent_original_env_version >= FT_LAYOUT_VERSION_14) {
+ toku_fill_dbt(&key, creation_time_key, strlen(creation_time_key));
+ toku_init_dbt(&val);
+ r = toku_db_get(persistent_environment, txn, &key, &val, 0);
+ assert_zero(r);
+ STATUS_VALUE(YDB_LAYER_TIME_CREATION) = toku_dtoh64((*(time_t*)val.data));
+ }
+
+ if (persistent_original_env_version != curr_env_version) {
+ // an upgrade was performed at some time, capture info about the upgrade
+
+ char * last_lsn_key = get_upgrade_last_lsn_key(curr_env_version);
+ toku_fill_dbt(&key, last_lsn_key, strlen(last_lsn_key));
+ toku_init_dbt(&val);
+ r = toku_db_get(persistent_environment, txn, &key, &val, 0);
+ assert_zero(r);
+ PERSISTENT_UPGRADE_STATUS_VALUE(PERSISTENT_UPGRADE_LAST_LSN_OF_V13) = toku_dtoh64(*(uint64_t*)val.data);
+
+ char * time_key = get_upgrade_time_key(curr_env_version);
+ toku_fill_dbt(&key, time_key, strlen(time_key));
+ toku_init_dbt(&val);
+ r = toku_db_get(persistent_environment, txn, &key, &val, 0);
+ assert_zero(r);
+ PERSISTENT_UPGRADE_STATUS_VALUE(PERSISTENT_UPGRADE_V14_TIME) = toku_dtoh64(*(time_t*)val.data);
+
+ char * footprint_key = get_upgrade_footprint_key(curr_env_version);
+ toku_fill_dbt(&key, footprint_key, strlen(footprint_key));
+ toku_init_dbt(&val);
+ r = toku_db_get(persistent_environment, txn, &key, &val, 0);
+ assert_zero(r);
+ PERSISTENT_UPGRADE_STATUS_VALUE(PERSISTENT_UPGRADE_V14_FOOTPRINT) = toku_dtoh64(*(uint64_t*)val.data);
+ }
+
+}
+
+// return 0 if log exists or ENOENT if log does not exist
+static int
+ydb_recover_log_exists(DB_ENV *env) {
+ int r = tokuft_recover_log_exists(env->i->real_log_dir);
+ return r;
+}
+
+// Validate that all required files are present, no side effects.
+// Return 0 if all is well, ENOENT if some files are present but at least one is missing,
+// other non-zero value if some other error occurs.
+// Set *valid_newenv if creating a new environment (all files missing).
+// (Note, if special dictionaries exist, then they were created transactionally and log should exist.)
+static int
+validate_env(DB_ENV * env, bool * valid_newenv, bool need_rollback_cachefile) {
+ int r;
+ bool expect_newenv = false; // set true if we expect to create a new env
+ toku_struct_stat buf;
+ char* path = NULL;
+
+ // Test for persistent environment
+ path = toku_construct_full_name(2, env->i->dir, toku_product_name_strings.environmentdictionary);
+ assert(path);
+ r = toku_stat(path, &buf);
+ if (r == 0) {
+ expect_newenv = false; // persistent info exists
+ }
+ else {
+ int stat_errno = get_error_errno();
+ if (stat_errno == ENOENT) {
+ expect_newenv = true;
+ r = 0;
+ }
+ else {
+ r = toku_ydb_do_error(env, stat_errno, "Unable to access persistent environment\n");
+ assert(r);
+ }
+ }
+ toku_free(path);
+
+ // Test for existence of rollback cachefile if it is expected to exist
+ if (r == 0 && need_rollback_cachefile) {
+ path = toku_construct_full_name(2, env->i->dir, toku_product_name_strings.rollback_cachefile);
+ assert(path);
+ r = toku_stat(path, &buf);
+ if (r == 0) {
+ if (expect_newenv) // rollback cachefile exists, but persistent env is missing
+ r = toku_ydb_do_error(env, ENOENT, "Persistent environment is missing\n");
+ }
+ else {
+ int stat_errno = get_error_errno();
+ if (stat_errno == ENOENT) {
+ if (!expect_newenv) // rollback cachefile is missing but persistent env exists
+ r = toku_ydb_do_error(env, ENOENT, "rollback cachefile directory is missing\n");
+ else
+ r = 0; // both rollback cachefile and persistent env are missing
+ }
+ else {
+ r = toku_ydb_do_error(env, stat_errno, "Unable to access rollback cachefile\n");
+ assert(r);
+ }
+ }
+ toku_free(path);
+ }
+
+ // Test for fileops directory
+ if (r == 0) {
+ path = toku_construct_full_name(2, env->i->dir, toku_product_name_strings.fileopsdirectory);
+ assert(path);
+ r = toku_stat(path, &buf);
+ if (r == 0) {
+ if (expect_newenv) // fileops directory exists, but persistent env is missing
+ r = toku_ydb_do_error(env, ENOENT, "Persistent environment is missing\n");
+ }
+ else {
+ int stat_errno = get_error_errno();
+ if (stat_errno == ENOENT) {
+ if (!expect_newenv) // fileops directory is missing but persistent env exists
+ r = toku_ydb_do_error(env, ENOENT, "Fileops directory is missing\n");
+ else
+ r = 0; // both fileops directory and persistent env are missing
+ }
+ else {
+ r = toku_ydb_do_error(env, stat_errno, "Unable to access fileops directory\n");
+ assert(r);
+ }
+ }
+ toku_free(path);
+ }
+
+ // Test for recovery log
+ if ((r == 0) && (env->i->open_flags & DB_INIT_LOG)) {
+ // if using transactions, test for existence of log
+ r = ydb_recover_log_exists(env); // return 0 or ENOENT
+ if (expect_newenv && (r != ENOENT))
+ r = toku_ydb_do_error(env, ENOENT, "Persistent environment information is missing (but log exists)\n");
+ else if (!expect_newenv && r == ENOENT)
+ r = toku_ydb_do_error(env, ENOENT, "Recovery log is missing (persistent environment information is present)\n");
+ else
+ r = 0;
+ }
+
+ if (r == 0)
+ *valid_newenv = expect_newenv;
+ else
+ *valid_newenv = false;
+ return r;
+}
+
+// The version of the environment (on disk) is the version of the recovery log.
+// If the recovery log is of the current version, then there is no upgrade to be done.
+// If the recovery log is of an old version, then replacing it with a new recovery log
+// of the current version is how the upgrade is done.
+// Note, the upgrade procedure takes a checkpoint, so we must release the ydb lock.
+static int
+ydb_maybe_upgrade_env (DB_ENV *env, LSN * last_lsn_of_clean_shutdown_read_from_log, bool * upgrade_in_progress) {
+ int r = 0;
+ if (env->i->open_flags & DB_INIT_TXN && env->i->open_flags & DB_INIT_LOG) {
+ r = toku_maybe_upgrade_log(env->i->dir, env->i->real_log_dir, last_lsn_of_clean_shutdown_read_from_log, upgrade_in_progress);
+ }
+ return r;
+}
+
+static void
+unlock_single_process(DB_ENV *env) {
+ int r;
+ r = toku_single_process_unlock(&env->i->envdir_lockfd);
+ lazy_assert_zero(r);
+ r = toku_single_process_unlock(&env->i->datadir_lockfd);
+ lazy_assert_zero(r);
+ r = toku_single_process_unlock(&env->i->logdir_lockfd);
+ lazy_assert_zero(r);
+ r = toku_single_process_unlock(&env->i->tmpdir_lockfd);
+ lazy_assert_zero(r);
+}
+
+// Open the environment.
+// If this is a new environment, then create the necessary files.
+// Return 0 on success, ENOENT if any of the expected necessary files are missing.
+// (The set of necessary files is defined in the function validate_env() above.)
+static int
+env_open(DB_ENV * env, const char *home, uint32_t flags, int mode) {
+ HANDLE_PANICKED_ENV(env);
+ int r;
+ bool newenv; // true iff creating a new environment
+ uint32_t unused_flags=flags;
+ CHECKPOINTER cp;
+ DB_TXN *txn = NULL;
+
+ if (env_opened(env)) {
+ r = toku_ydb_do_error(env, EINVAL, "The environment is already open\n");
+ goto cleanup;
+ }
+
+ if (toku_os_huge_pages_enabled()) {
+ r = toku_ydb_do_error(env, TOKUDB_HUGE_PAGES_ENABLED,
+ "Huge pages are enabled, disable them before continuing\n");
+ goto cleanup;
+ }
+
+ most_recent_env = NULL;
+
+ assert(sizeof(time_t) == sizeof(uint64_t));
+
+ HANDLE_EXTRA_FLAGS(env, flags,
+ DB_CREATE|DB_PRIVATE|DB_INIT_LOG|DB_INIT_TXN|DB_RECOVER|DB_INIT_MPOOL|DB_INIT_LOCK|DB_THREAD);
+
+ // DB_CREATE means create if env does not exist, and PerconaFT requires it because
+ // PerconaFT requries DB_PRIVATE.
+ if ((flags & DB_PRIVATE) && !(flags & DB_CREATE)) {
+ r = toku_ydb_do_error(env, ENOENT, "DB_PRIVATE requires DB_CREATE (seems gratuitous to us, but that's BDB's behavior\n");
+ goto cleanup;
+ }
+
+ if (!(flags & DB_PRIVATE)) {
+ r = toku_ydb_do_error(env, ENOENT, "PerconaFT requires DB_PRIVATE\n");
+ goto cleanup;
+ }
+
+ if ((flags & DB_INIT_LOG) && !(flags & DB_INIT_TXN)) {
+ r = toku_ydb_do_error(env, EINVAL, "PerconaFT requires transactions for logging\n");
+ goto cleanup;
+ }
+
+ if (!home) home = ".";
+
+ // Verify that the home exists.
+ toku_struct_stat buf;
+ r = toku_stat(home, &buf);
+ if (r != 0) {
+ int e = get_error_errno();
+ r = toku_ydb_do_error(env, e, "Error from toku_stat(\"%s\",...)\n", home);
+ goto cleanup;
+ }
+ unused_flags &= ~DB_PRIVATE;
+
+ if (env->i->dir) {
+ toku_free(env->i->dir);
+ }
+ env->i->dir = toku_strdup(home);
+ if (env->i->dir == 0) {
+ r = toku_ydb_do_error(env, ENOMEM, "Out of memory\n");
+ goto cleanup;
+ }
+ env->i->open_flags = flags;
+ env->i->open_mode = mode;
+
+ env_setup_real_data_dir(env);
+ env_setup_real_log_dir(env);
+ env_setup_real_tmp_dir(env);
+
+ r = toku_single_process_lock(env->i->dir, "environment", &env->i->envdir_lockfd);
+ if (r!=0) goto cleanup;
+ r = toku_single_process_lock(env->i->real_data_dir, "data", &env->i->datadir_lockfd);
+ if (r!=0) goto cleanup;
+ r = toku_single_process_lock(env->i->real_log_dir, "logs", &env->i->logdir_lockfd);
+ if (r!=0) goto cleanup;
+ r = toku_single_process_lock(env->i->real_tmp_dir, "temp", &env->i->tmpdir_lockfd);
+ if (r!=0) goto cleanup;
+
+ bool need_rollback_cachefile;
+ need_rollback_cachefile = false;
+ if (flags & (DB_INIT_TXN | DB_INIT_LOG)) {
+ need_rollback_cachefile = true;
+ }
+
+ ydb_layer_status_init(); // do this before possibly upgrading, so upgrade work is counted in status counters
+
+ LSN last_lsn_of_clean_shutdown_read_from_log;
+ last_lsn_of_clean_shutdown_read_from_log = ZERO_LSN;
+ bool upgrade_in_progress;
+ upgrade_in_progress = false;
+ r = ydb_maybe_upgrade_env(env, &last_lsn_of_clean_shutdown_read_from_log, &upgrade_in_progress);
+ if (r!=0) goto cleanup;
+
+ if (upgrade_in_progress) {
+ // Delete old rollback file. There was a clean shutdown, so it has nothing useful,
+ // and there is no value in upgrading it. It is simpler to just create a new one.
+ char* rollback_filename = toku_construct_full_name(2, env->i->dir, toku_product_name_strings.rollback_cachefile);
+ assert(rollback_filename);
+ r = unlink(rollback_filename);
+ if (r != 0) {
+ assert(get_error_errno() == ENOENT);
+ }
+ toku_free(rollback_filename);
+ need_rollback_cachefile = false; // we're not expecting it to exist now
+ }
+
+ r = validate_env(env, &newenv, need_rollback_cachefile); // make sure that environment is either new or complete
+ if (r != 0) goto cleanup;
+
+ unused_flags &= ~DB_INIT_TXN & ~DB_INIT_LOG;
+
+ // do recovery only if there exists a log and recovery is requested
+ // otherwise, a log is created when the logger is opened later
+ if (!newenv) {
+ if (flags & DB_INIT_LOG) {
+ // the log does exist
+ if (flags & DB_RECOVER) {
+ r = ydb_do_recovery(env);
+ if (r != 0) goto cleanup;
+ } else {
+ // the log is required to have clean shutdown if recovery is not requested
+ r = needs_recovery(env);
+ if (r != 0) goto cleanup;
+ }
+ }
+ }
+
+ toku_loader_cleanup_temp_files(env);
+
+ if (flags & (DB_INIT_TXN | DB_INIT_LOG)) {
+ assert(env->i->logger);
+ toku_logger_write_log_files(env->i->logger, (bool)((flags & DB_INIT_LOG) != 0));
+ if (!toku_logger_is_open(env->i->logger)) {
+ r = toku_logger_open(env->i->real_log_dir, env->i->logger);
+ if (r!=0) {
+ toku_ydb_do_error(env, r, "Could not open logger\n");
+ }
+ }
+ } else {
+ r = toku_logger_close(&env->i->logger); // if no logging system, then kill the logger
+ assert_zero(r);
+ }
+
+ unused_flags &= ~DB_INIT_MPOOL; // we always init an mpool.
+ unused_flags &= ~DB_CREATE; // we always do DB_CREATE
+ unused_flags &= ~DB_INIT_LOCK; // we check this later (e.g. in db->open)
+ unused_flags &= ~DB_RECOVER;
+
+// This is probably correct, but it will be pain...
+// if ((flags & DB_THREAD)==0) {
+// r = toku_ydb_do_error(env, EINVAL, "PerconaFT requires DB_THREAD");
+// goto cleanup;
+// }
+ unused_flags &= ~DB_THREAD;
+
+ if (unused_flags!=0) {
+ r = toku_ydb_do_error(env, EINVAL, "Extra flags not understood by tokuft: %u\n", unused_flags);
+ goto cleanup;
+ }
+
+ if (env->i->cachetable==NULL) {
+ // If we ran recovery then the cachetable should be set here.
+ r = toku_cachetable_create_ex(&env->i->cachetable, env->i->cachetable_size,
+ env->i->client_pool_threads,
+ env->i->cachetable_pool_threads,
+ env->i->checkpoint_pool_threads,
+ ZERO_LSN, env->i->logger);
+ if (r != 0) {
+ r = toku_ydb_do_error(env, r, "Cant create a cachetable\n");
+ goto cleanup;
+ }
+ }
+
+ toku_cachetable_set_env_dir(env->i->cachetable, env->i->dir);
+
+ int using_txns;
+ using_txns = env->i->open_flags & DB_INIT_TXN;
+ if (env->i->logger) {
+ // if this is a newborn env or if this is an upgrade, then create a brand new rollback file
+ assert (using_txns);
+ toku_logger_set_cachetable(env->i->logger, env->i->cachetable);
+ if (!toku_logger_rollback_is_open(env->i->logger)) {
+ bool create_new_rollback_file = newenv | upgrade_in_progress;
+ r = toku_logger_open_rollback(env->i->logger, env->i->cachetable, create_new_rollback_file);
+ if (r != 0) {
+ r = toku_ydb_do_error(env, r, "Cant open rollback\n");
+ goto cleanup;
+ }
+ }
+ }
+
+ if (using_txns) {
+ r = toku_txn_begin(env, 0, &txn, 0);
+ assert_zero(r);
+ }
+
+ {
+ r = toku_db_create(&env->i->persistent_environment, env, 0);
+ assert_zero(r);
+ r = toku_db_use_builtin_key_cmp(env->i->persistent_environment);
+ assert_zero(r);
+ r = toku_db_open_iname(env->i->persistent_environment, txn, toku_product_name_strings.environmentdictionary, DB_CREATE, mode);
+ if (r != 0) {
+ r = toku_ydb_do_error(env, r, "Cant open persistent env\n");
+ goto cleanup;
+ }
+ if (newenv) {
+ // create new persistent_environment
+ DBT key, val;
+ uint32_t persistent_original_env_version = FT_LAYOUT_VERSION;
+ const uint32_t environment_version = toku_htod32(persistent_original_env_version);
+
+ toku_fill_dbt(&key, orig_env_ver_key, strlen(orig_env_ver_key));
+ toku_fill_dbt(&val, &environment_version, sizeof(environment_version));
+ r = toku_db_put(env->i->persistent_environment, txn, &key, &val, 0, false);
+ assert_zero(r);
+
+ toku_fill_dbt(&key, curr_env_ver_key, strlen(curr_env_ver_key));
+ toku_fill_dbt(&val, &environment_version, sizeof(environment_version));
+ r = toku_db_put(env->i->persistent_environment, txn, &key, &val, 0, false);
+ assert_zero(r);
+
+ time_t creation_time_d = toku_htod64(time(NULL));
+ toku_fill_dbt(&key, creation_time_key, strlen(creation_time_key));
+ toku_fill_dbt(&val, &creation_time_d, sizeof(creation_time_d));
+ r = toku_db_put(env->i->persistent_environment, txn, &key, &val, 0, false);
+ assert_zero(r);
+ }
+ else {
+ r = maybe_upgrade_persistent_environment_dictionary(env, txn, last_lsn_of_clean_shutdown_read_from_log);
+ assert_zero(r);
+ }
+ capture_persistent_env_contents(env, txn);
+ }
+ {
+ r = toku_db_create(&env->i->directory, env, 0);
+ assert_zero(r);
+ r = toku_db_use_builtin_key_cmp(env->i->directory);
+ assert_zero(r);
+ r = toku_db_open_iname(env->i->directory, txn, toku_product_name_strings.fileopsdirectory, DB_CREATE, mode);
+ if (r != 0) {
+ r = toku_ydb_do_error(env, r, "Cant open %s\n", toku_product_name_strings.fileopsdirectory);
+ goto cleanup;
+ }
+ }
+ if (using_txns) {
+ r = locked_txn_commit(txn, 0);
+ assert_zero(r);
+ txn = NULL;
+ }
+ cp = toku_cachetable_get_checkpointer(env->i->cachetable);
+ r = toku_checkpoint(cp, env->i->logger, NULL, NULL, NULL, NULL, STARTUP_CHECKPOINT);
+ assert_zero(r);
+ env_fs_poller(env); // get the file system state at startup
+ r = env_fs_init_minicron(env);
+ if (r != 0) {
+ r = toku_ydb_do_error(env, r, "Cant create fs minicron\n");
+ goto cleanup;
+ }
+ r = env_fsync_log_cron_init(env);
+ if (r != 0) {
+ r = toku_ydb_do_error(env, r, "Cant create fsync log minicron\n");
+ goto cleanup;
+ }
+cleanup:
+ if (r!=0) {
+ if (txn) {
+ locked_txn_abort(txn);
+ }
+ if (env && env->i) {
+ unlock_single_process(env);
+ }
+ }
+ if (r == 0) {
+ set_errno(0); // tabula rasa. If there's a crash after env was successfully opened, no misleading errno will have been left around by this code.
+ most_recent_env = env;
+ uint64_t num_rows;
+ env_get_engine_status_num_rows(env, &num_rows);
+ toku_assert_set_fpointers(toku_maybe_get_engine_status_text, toku_maybe_err_engine_status, toku_maybe_set_env_panic, num_rows);
+ }
+ return r;
+}
+
+static int
+env_close(DB_ENV * env, uint32_t flags) {
+ int r = 0;
+ const char * err_msg = NULL;
+ bool clean_shutdown = true;
+
+ if (flags & TOKUFT_DIRTY_SHUTDOWN) {
+ clean_shutdown = false;
+ flags &= ~TOKUFT_DIRTY_SHUTDOWN;
+ }
+
+ most_recent_env = NULL; // Set most_recent_env to NULL so that we don't have a dangling pointer (and if there's an error, the toku assert code would try to look at the env.)
+
+ // if panicked, or if any open transactions, or any open dbs, then do nothing.
+
+ if (toku_env_is_panicked(env)) {
+ goto panic_and_quit_early;
+ }
+ if (env->i->logger && toku_logger_txns_exist(env->i->logger)) {
+ err_msg = "Cannot close environment due to open transactions\n";
+ r = toku_ydb_do_error(env, EINVAL, "%s", err_msg);
+ goto panic_and_quit_early;
+ }
+ if (env->i->open_dbs_by_dname) { //Verify that there are no open dbs.
+ if (env->i->open_dbs_by_dname->size() > 0) {
+ err_msg = "Cannot close environment due to open DBs\n";
+ r = toku_ydb_do_error(env, EINVAL, "%s", err_msg);
+ goto panic_and_quit_early;
+ }
+ }
+ if (env->i->persistent_environment) {
+ r = toku_db_close(env->i->persistent_environment);
+ if (r) {
+ err_msg = "Cannot close persistent environment dictionary (DB->close error)\n";
+ toku_ydb_do_error(env, r, "%s", err_msg);
+ goto panic_and_quit_early;
+ }
+ }
+ if (env->i->directory) {
+ r = toku_db_close(env->i->directory);
+ if (r) {
+ err_msg = "Cannot close Directory dictionary (DB->close error)\n";
+ toku_ydb_do_error(env, r, "%s", err_msg);
+ goto panic_and_quit_early;
+ }
+ }
+ env_fsync_log_cron_destroy(env);
+ if (env->i->cachetable) {
+ toku_cachetable_prepare_close(env->i->cachetable);
+ toku_cachetable_minicron_shutdown(env->i->cachetable);
+ if (env->i->logger) {
+ CHECKPOINTER cp = nullptr;
+ if (clean_shutdown) {
+ cp = toku_cachetable_get_checkpointer(env->i->cachetable);
+ r = toku_checkpoint(cp, env->i->logger, NULL, NULL, NULL, NULL, SHUTDOWN_CHECKPOINT);
+ if (r) {
+ err_msg = "Cannot close environment (error during checkpoint)\n";
+ toku_ydb_do_error(env, r, "%s", err_msg);
+ goto panic_and_quit_early;
+ }
+ }
+ toku_logger_close_rollback_check_empty(env->i->logger, clean_shutdown);
+ if (clean_shutdown) {
+ //Do a second checkpoint now that the rollback cachefile is closed.
+ r = toku_checkpoint(cp, env->i->logger, NULL, NULL, NULL, NULL, SHUTDOWN_CHECKPOINT);
+ if (r) {
+ err_msg = "Cannot close environment (error during checkpoint)\n";
+ toku_ydb_do_error(env, r, "%s", err_msg);
+ goto panic_and_quit_early;
+ }
+ toku_logger_shutdown(env->i->logger);
+ }
+ }
+ toku_cachetable_close(&env->i->cachetable);
+ }
+ if (env->i->logger) {
+ r = toku_logger_close(&env->i->logger);
+ if (r) {
+ err_msg = "Cannot close environment (logger close error)\n";
+ env->i->logger = NULL;
+ toku_ydb_do_error(env, r, "%s", err_msg);
+ goto panic_and_quit_early;
+ }
+ }
+ // Even if nothing else went wrong, but we were panicked, then raise an error.
+ // But if something else went wrong then raise that error (above)
+ if (toku_env_is_panicked(env)) {
+ goto panic_and_quit_early;
+ } else {
+ assert(env->i->panic_string == 0);
+ }
+
+ env_fs_destroy(env);
+ env->i->ltm.destroy();
+ if (env->i->data_dir)
+ toku_free(env->i->data_dir);
+ if (env->i->lg_dir)
+ toku_free(env->i->lg_dir);
+ if (env->i->tmp_dir)
+ toku_free(env->i->tmp_dir);
+ if (env->i->real_data_dir)
+ toku_free(env->i->real_data_dir);
+ if (env->i->real_log_dir)
+ toku_free(env->i->real_log_dir);
+ if (env->i->real_tmp_dir)
+ toku_free(env->i->real_tmp_dir);
+ if (env->i->open_dbs_by_dname) {
+ env->i->open_dbs_by_dname->destroy();
+ toku_free(env->i->open_dbs_by_dname);
+ }
+ if (env->i->open_dbs_by_dict_id) {
+ env->i->open_dbs_by_dict_id->destroy();
+ toku_free(env->i->open_dbs_by_dict_id);
+ }
+ if (env->i->dir)
+ toku_free(env->i->dir);
+ toku_pthread_rwlock_destroy(&env->i->open_dbs_rwlock);
+
+ // Immediately before freeing internal environment unlock the directories
+ unlock_single_process(env);
+ toku_free(env->i);
+ toku_free(env);
+ toku_sync_fetch_and_add(&tokuft_num_envs, -1);
+ if (flags != 0) {
+ r = EINVAL;
+ }
+ return r;
+
+panic_and_quit_early:
+ //release lock files.
+ unlock_single_process(env);
+ //r is the panic error
+ if (toku_env_is_panicked(env)) {
+ char *panic_string = env->i->panic_string;
+ r = toku_ydb_do_error(env, toku_env_is_panicked(env), "Cannot close environment due to previous error: %s\n", panic_string);
+ }
+ else {
+ env_panic(env, r, err_msg);
+ }
+ return r;
+}
+
+static int
+env_log_archive(DB_ENV * env, char **list[], uint32_t flags) {
+ return toku_logger_log_archive(env->i->logger, list, flags);
+}
+
+static int
+env_log_flush(DB_ENV * env, const DB_LSN * lsn __attribute__((__unused__))) {
+ HANDLE_PANICKED_ENV(env);
+ // do nothing if no logger
+ if (env->i->logger) {
+ // We just flush everything. MySQL uses lsn == 0 which means flush everything.
+ // For anyone else using the log, it is correct to flush too much, so we are OK.
+ toku_logger_fsync(env->i->logger);
+ }
+ return 0;
+}
+
+static int
+env_set_cachesize(DB_ENV * env, uint32_t gbytes, uint32_t bytes, int ncache) {
+ HANDLE_PANICKED_ENV(env);
+ if (ncache != 1) {
+ return EINVAL;
+ }
+ uint64_t cs64 = ((uint64_t) gbytes << 30) + bytes;
+ unsigned long cs = cs64;
+ if (cs64 > cs) {
+ return EINVAL;
+ }
+ env->i->cachetable_size = cs;
+ return 0;
+}
+
+static int
+env_set_client_pool_threads(DB_ENV * env, uint32_t threads) {
+ HANDLE_PANICKED_ENV(env);
+ env->i->client_pool_threads = threads;
+ return 0;
+}
+
+static int
+env_set_cachetable_pool_threads(DB_ENV * env, uint32_t threads) {
+ HANDLE_PANICKED_ENV(env);
+ env->i->cachetable_pool_threads = threads;
+ return 0;
+}
+
+static int
+env_set_checkpoint_pool_threads(DB_ENV * env, uint32_t threads) {
+ HANDLE_PANICKED_ENV(env);
+ env->i->checkpoint_pool_threads = threads;
+ return 0;
+}
+
+static int env_dbremove(DB_ENV * env, DB_TXN *txn, const char *fname, const char *dbname, uint32_t flags);
+
+static int
+locked_env_dbremove(DB_ENV * env, DB_TXN *txn, const char *fname, const char *dbname, uint32_t flags) {
+ int ret, r;
+ HANDLE_ILLEGAL_WORKING_PARENT_TXN(env, txn);
+ HANDLE_READ_ONLY_TXN(txn);
+
+ DB_TXN *child_txn = NULL;
+ int using_txns = env->i->open_flags & DB_INIT_TXN;
+ if (using_txns) {
+ ret = toku_txn_begin(env, txn, &child_txn, 0);
+ lazy_assert_zero(ret);
+ }
+
+ // cannot begin a checkpoint
+ toku_multi_operation_client_lock();
+ r = env_dbremove(env, child_txn, fname, dbname, flags);
+ toku_multi_operation_client_unlock();
+
+ if (using_txns) {
+ if (r == 0) {
+ ret = locked_txn_commit(child_txn, 0);
+ lazy_assert_zero(ret);
+ } else {
+ ret = locked_txn_abort(child_txn);
+ lazy_assert_zero(ret);
+ }
+ }
+ return r;
+}
+
+static int env_dbrename(DB_ENV *env, DB_TXN *txn, const char *fname, const char *dbname, const char *newname, uint32_t flags);
+
+static int
+locked_env_dbrename(DB_ENV *env, DB_TXN *txn, const char *fname, const char *dbname, const char *newname, uint32_t flags) {
+ int ret, r;
+ HANDLE_READ_ONLY_TXN(txn);
+ HANDLE_ILLEGAL_WORKING_PARENT_TXN(env, txn);
+
+ DB_TXN *child_txn = NULL;
+ int using_txns = env->i->open_flags & DB_INIT_TXN;
+ if (using_txns) {
+ ret = toku_txn_begin(env, txn, &child_txn, 0);
+ lazy_assert_zero(ret);
+ }
+
+ // cannot begin a checkpoint
+ toku_multi_operation_client_lock();
+ r = env_dbrename(env, child_txn, fname, dbname, newname, flags);
+ toku_multi_operation_client_unlock();
+
+ if (using_txns) {
+ if (r == 0) {
+ ret = locked_txn_commit(child_txn, 0);
+ lazy_assert_zero(ret);
+ } else {
+ ret = locked_txn_abort(child_txn);
+ lazy_assert_zero(ret);
+ }
+ }
+ return r;
+}
+
+#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
+
+static int
+env_get_cachesize(DB_ENV * env, uint32_t *gbytes, uint32_t *bytes, int *ncache) {
+ HANDLE_PANICKED_ENV(env);
+ *gbytes = env->i->cachetable_size >> 30;
+ *bytes = env->i->cachetable_size & ((1<<30)-1);
+ *ncache = 1;
+ return 0;
+}
+
+#endif
+
+static int
+env_set_data_dir(DB_ENV * env, const char *dir) {
+ HANDLE_PANICKED_ENV(env);
+ int r;
+
+ if (env_opened(env) || !dir) {
+ r = toku_ydb_do_error(env, EINVAL, "You cannot set the data dir after opening the env\n");
+ }
+ else if (env->i->data_dir)
+ r = toku_ydb_do_error(env, EINVAL, "You cannot set the data dir more than once.\n");
+ else {
+ env->i->data_dir = toku_strdup(dir);
+ if (env->i->data_dir==NULL) {
+ assert(get_error_errno() == ENOMEM);
+ r = toku_ydb_do_error(env, ENOMEM, "Out of memory\n");
+ }
+ else r = 0;
+ }
+ return r;
+}
+
+static void
+env_set_errcall(DB_ENV * env, toku_env_errcall_t errcall) {
+ env->i->errcall = errcall;
+}
+
+static void
+env_set_errfile(DB_ENV*env, FILE*errfile) {
+ env->i->errfile = errfile;
+}
+
+static void
+env_set_errpfx(DB_ENV * env, const char *errpfx) {
+ env->i->errpfx = errpfx;
+}
+
+static int
+env_set_flags(DB_ENV * env, uint32_t flags, int onoff) {
+ HANDLE_PANICKED_ENV(env);
+
+ uint32_t change = 0;
+ if (flags & DB_AUTO_COMMIT) {
+ change |= DB_AUTO_COMMIT;
+ flags &= ~DB_AUTO_COMMIT;
+ }
+ if (flags != 0 && onoff) {
+ return toku_ydb_do_error(env, EINVAL, "PerconaFT does not (yet) support any nonzero ENV flags other than DB_AUTO_COMMIT\n");
+ }
+ if (onoff) env->i->open_flags |= change;
+ else env->i->open_flags &= ~change;
+ return 0;
+}
+
+static int
+env_set_lg_bsize(DB_ENV * env, uint32_t bsize) {
+ HANDLE_PANICKED_ENV(env);
+ return toku_logger_set_lg_bsize(env->i->logger, bsize);
+}
+
+static int
+env_set_lg_dir(DB_ENV * env, const char *dir) {
+ HANDLE_PANICKED_ENV(env);
+ if (env_opened(env)) {
+ return toku_ydb_do_error(env, EINVAL, "Cannot set log dir after opening the env\n");
+ }
+
+ if (env->i->lg_dir) toku_free(env->i->lg_dir);
+ if (dir) {
+ env->i->lg_dir = toku_strdup(dir);
+ if (!env->i->lg_dir) {
+ return toku_ydb_do_error(env, ENOMEM, "Out of memory\n");
+ }
+ }
+ else env->i->lg_dir = NULL;
+ return 0;
+}
+
+static int
+env_set_lg_max(DB_ENV * env, uint32_t lg_max) {
+ HANDLE_PANICKED_ENV(env);
+ return toku_logger_set_lg_max(env->i->logger, lg_max);
+}
+
+static int
+env_get_lg_max(DB_ENV * env, uint32_t *lg_maxp) {
+ HANDLE_PANICKED_ENV(env);
+ return toku_logger_get_lg_max(env->i->logger, lg_maxp);
+}
+
+static int
+env_set_lk_detect(DB_ENV * env, uint32_t UU(detect)) {
+ HANDLE_PANICKED_ENV(env);
+ return toku_ydb_do_error(env, EINVAL, "PerconaFT does not (yet) support set_lk_detect\n");
+}
+
+static int
+env_set_lk_max_memory(DB_ENV *env, uint64_t lock_memory_limit) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (env_opened(env)) {
+ r = EINVAL;
+ } else {
+ r = env->i->ltm.set_max_lock_memory(lock_memory_limit);
+ }
+ return r;
+}
+
+static int
+env_get_lk_max_memory(DB_ENV *env, uint64_t *lk_maxp) {
+ HANDLE_PANICKED_ENV(env);
+ uint32_t max_lock_memory = env->i->ltm.get_max_lock_memory();
+ *lk_maxp = max_lock_memory;
+ return 0;
+}
+
+//void toku__env_set_noticecall (DB_ENV *env, void (*noticecall)(DB_ENV *, db_notices)) {
+// env->i->noticecall = noticecall;
+//}
+
+static int
+env_set_tmp_dir(DB_ENV * env, const char *tmp_dir) {
+ HANDLE_PANICKED_ENV(env);
+ if (env_opened(env)) {
+ return toku_ydb_do_error(env, EINVAL, "Cannot set the tmp dir after opening an env\n");
+ }
+ if (!tmp_dir) {
+ return toku_ydb_do_error(env, EINVAL, "Tmp dir bust be non-null\n");
+ }
+ if (env->i->tmp_dir)
+ toku_free(env->i->tmp_dir);
+ env->i->tmp_dir = toku_strdup(tmp_dir);
+ return env->i->tmp_dir ? 0 : ENOMEM;
+}
+
+static int
+env_set_verbose(DB_ENV * env, uint32_t UU(which), int UU(onoff)) {
+ HANDLE_PANICKED_ENV(env);
+ return 1;
+}
+
+static int
+toku_env_txn_checkpoint(DB_ENV * env, uint32_t kbyte __attribute__((__unused__)), uint32_t min __attribute__((__unused__)), uint32_t flags __attribute__((__unused__))) {
+ CHECKPOINTER cp = toku_cachetable_get_checkpointer(env->i->cachetable);
+ int r = toku_checkpoint(cp, env->i->logger,
+ checkpoint_callback_f, checkpoint_callback_extra,
+ checkpoint_callback2_f, checkpoint_callback2_extra,
+ CLIENT_CHECKPOINT);
+ if (r) {
+ // Panicking the whole environment may be overkill, but I'm not sure what else to do.
+ env_panic(env, r, "checkpoint error\n");
+ toku_ydb_do_error(env, r, "Checkpoint\n");
+ }
+ return r;
+}
+
+static int
+env_txn_stat(DB_ENV * env, DB_TXN_STAT ** UU(statp), uint32_t UU(flags)) {
+ HANDLE_PANICKED_ENV(env);
+ return 1;
+}
+
+//
+// We can assume the client calls this function right after recovery
+// to return a list of prepared transactions to the user. When called,
+// we can assume that no other work is being done in the system,
+// as we are in the state of being after recovery,
+// but before client operations should commence
+//
+static int
+env_txn_xa_recover (DB_ENV *env, TOKU_XA_XID xids[/*count*/], long count, /*out*/ long *retp, uint32_t flags) {
+ struct tokulogger_preplist *MALLOC_N(count,preps);
+ int r = toku_logger_recover_txn(env->i->logger, preps, count, retp, flags);
+ if (r==0) {
+ assert(*retp<=count);
+ for (int i=0; i<*retp; i++) {
+ xids[i] = preps[i].xid;
+ }
+ }
+ toku_free(preps);
+ return r;
+}
+
+//
+// We can assume the client calls this function right after recovery
+// to return a list of prepared transactions to the user. When called,
+// we can assume that no other work is being done in the system,
+// as we are in the state of being after recovery,
+// but before client operations should commence
+//
+static int
+env_txn_recover (DB_ENV *env, DB_PREPLIST preplist[/*count*/], long count, /*out*/ long *retp, uint32_t flags) {
+ struct tokulogger_preplist *MALLOC_N(count,preps);
+ int r = toku_logger_recover_txn(env->i->logger, preps, count, retp, flags);
+ if (r==0) {
+ assert(*retp<=count);
+ for (int i=0; i<*retp; i++) {
+ preplist[i].txn = preps[i].txn;
+ memcpy(preplist[i].gid, preps[i].xid.data, preps[i].xid.gtrid_length + preps[i].xid.bqual_length);
+ }
+ }
+ toku_free(preps);
+ return r;
+}
+
+static int
+env_get_txn_from_xid (DB_ENV *env, /*in*/ TOKU_XA_XID *xid, /*out*/ DB_TXN **txnp) {
+ return toku_txn_manager_get_root_txn_from_xid(toku_logger_get_txn_manager(env->i->logger), xid, txnp);
+}
+
+static int
+env_checkpointing_set_period(DB_ENV * env, uint32_t seconds) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) {
+ r = EINVAL;
+ } else {
+ toku_set_checkpoint_period(env->i->cachetable, seconds);
+ }
+ return r;
+}
+
+static int
+env_cleaner_set_period(DB_ENV * env, uint32_t seconds) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) {
+ r = EINVAL;
+ } else {
+ toku_set_cleaner_period(env->i->cachetable, seconds);
+ }
+ return r;
+}
+
+static int
+env_cleaner_set_iterations(DB_ENV * env, uint32_t iterations) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) {
+ r = EINVAL;
+ } else {
+ toku_set_cleaner_iterations(env->i->cachetable, iterations);
+ }
+ return r;
+}
+
+static int
+env_create_loader(DB_ENV *env,
+ DB_TXN *txn,
+ DB_LOADER **blp,
+ DB *src_db,
+ int N,
+ DB *dbs[],
+ uint32_t db_flags[/*N*/],
+ uint32_t dbt_flags[/*N*/],
+ uint32_t loader_flags) {
+ int r = toku_loader_create_loader(env, txn, blp, src_db, N, dbs, db_flags, dbt_flags, loader_flags, true);
+ return r;
+}
+
+static int
+env_checkpointing_get_period(DB_ENV * env, uint32_t *seconds) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else
+ *seconds = toku_get_checkpoint_period_unlocked(env->i->cachetable);
+ return r;
+}
+
+static int
+env_cleaner_get_period(DB_ENV * env, uint32_t *seconds) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else
+ *seconds = toku_get_cleaner_period_unlocked(env->i->cachetable);
+ return r;
+}
+
+static int
+env_cleaner_get_iterations(DB_ENV * env, uint32_t *iterations) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else
+ *iterations = toku_get_cleaner_iterations(env->i->cachetable);
+ return r;
+}
+
+static int
+env_evictor_set_enable_partial_eviction(DB_ENV* env, bool enabled) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else toku_set_enable_partial_eviction(env->i->cachetable, enabled);
+ return r;
+}
+
+static int
+env_evictor_get_enable_partial_eviction(DB_ENV* env, bool *enabled) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else *enabled = toku_get_enable_partial_eviction(env->i->cachetable);
+ return r;
+}
+
+static int
+env_checkpointing_postpone(DB_ENV * env) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else toku_checkpoint_safe_client_lock();
+ return r;
+}
+
+static int
+env_checkpointing_resume(DB_ENV * env) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else toku_checkpoint_safe_client_unlock();
+ return r;
+}
+
+static int
+env_checkpointing_begin_atomic_operation(DB_ENV * env) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else toku_multi_operation_client_lock();
+ return r;
+}
+
+static int
+env_checkpointing_end_atomic_operation(DB_ENV * env) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (!env_opened(env)) r = EINVAL;
+ else toku_multi_operation_client_unlock();
+ return r;
+}
+
+static int
+env_set_default_bt_compare(DB_ENV * env, int (*bt_compare) (DB *, const DBT *, const DBT *)) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (env_opened(env)) r = EINVAL;
+ else {
+ env->i->bt_compare = bt_compare;
+ }
+ return r;
+}
+
+static void
+env_set_update (DB_ENV *env, int (*update_function)(DB *, const DBT *key, const DBT *old_val, const DBT *extra, void (*set_val)(const DBT *new_val, void *set_extra), void *set_extra)) {
+ env->i->update_function = update_function;
+}
+
+static int
+env_set_generate_row_callback_for_put(DB_ENV *env, generate_row_for_put_func generate_row_for_put) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (env_opened(env)) r = EINVAL;
+ else {
+ env->i->generate_row_for_put = generate_row_for_put;
+ }
+ return r;
+}
+
+static int
+env_set_generate_row_callback_for_del(DB_ENV *env, generate_row_for_del_func generate_row_for_del) {
+ HANDLE_PANICKED_ENV(env);
+ int r = 0;
+ if (env_opened(env)) r = EINVAL;
+ else {
+ env->i->generate_row_for_del = generate_row_for_del;
+ }
+ return r;
+}
+static int
+env_set_redzone(DB_ENV *env, int redzone) {
+ HANDLE_PANICKED_ENV(env);
+ int r;
+ if (env_opened(env))
+ r = EINVAL;
+ else {
+ env->i->redzone = redzone;
+ r = 0;
+ }
+ return r;
+}
+
+static int env_get_lock_timeout(DB_ENV *env, uint64_t *lock_timeout_msec) {
+ uint64_t t = env->i->default_lock_timeout_msec;
+ if (env->i->get_lock_timeout_callback)
+ t = env->i->get_lock_timeout_callback(t);
+ *lock_timeout_msec = t;
+ return 0;
+}
+
+static int env_set_lock_timeout(DB_ENV *env, uint64_t default_lock_timeout_msec, uint64_t (*get_lock_timeout_callback)(uint64_t default_lock_timeout_msec)) {
+ env->i->default_lock_timeout_msec = default_lock_timeout_msec;
+ env->i->get_lock_timeout_callback = get_lock_timeout_callback;
+ return 0;
+}
+
+static int
+env_set_lock_timeout_callback(DB_ENV *env, lock_timeout_callback callback) {
+ env->i->lock_wait_timeout_callback = callback;
+ return 0;
+}
+
+static void
+format_time(const time_t *timer, char *buf) {
+ ctime_r(timer, buf);
+ size_t len = strlen(buf);
+ assert(len < 26);
+ char end;
+
+ assert(len>=1);
+ end = buf[len-1];
+ while (end == '\n' || end == '\r') {
+ buf[len-1] = '\0';
+ len--;
+ assert(len>=1);
+ end = buf[len-1];
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+// Local definition of status information from portability layer, which should not include db.h.
+// Local status structs are used to concentrate file system information collected from various places
+// and memory information collected from memory.c.
+//
+typedef enum {
+ FS_ENOSPC_REDZONE_STATE = 0, // possible values are enumerated by fs_redzone_state
+ FS_ENOSPC_THREADS_BLOCKED, // how many threads currently blocked on ENOSPC
+ FS_ENOSPC_REDZONE_CTR, // number of operations rejected by enospc prevention (red zone)
+ FS_ENOSPC_MOST_RECENT, // most recent time that file system was completely full
+ FS_ENOSPC_COUNT, // total number of times ENOSPC was returned from an attempt to write
+ FS_FSYNC_TIME,
+ FS_FSYNC_COUNT,
+ FS_LONG_FSYNC_TIME,
+ FS_LONG_FSYNC_COUNT,
+ FS_STATUS_NUM_ROWS, // must be last
+} fs_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[FS_STATUS_NUM_ROWS];
+} FS_STATUS_S, *FS_STATUS;
+
+static FS_STATUS_S fsstat;
+
+#define FS_STATUS_INIT(k,c,t,l,inc) TOKUFT_STATUS_INIT(fsstat, k, c, t, "filesystem: " l, inc)
+
+static void
+fs_status_init(void) {
+ FS_STATUS_INIT(FS_ENOSPC_REDZONE_STATE, nullptr, FS_STATE, "ENOSPC redzone state", TOKU_ENGINE_STATUS);
+ FS_STATUS_INIT(FS_ENOSPC_THREADS_BLOCKED, FILESYSTEM_THREADS_BLOCKED_BY_FULL_DISK, UINT64, "threads currently blocked by full disk", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ FS_STATUS_INIT(FS_ENOSPC_REDZONE_CTR, nullptr, UINT64, "number of operations rejected by enospc prevention (red zone)", TOKU_ENGINE_STATUS);
+ FS_STATUS_INIT(FS_ENOSPC_MOST_RECENT, nullptr, UNIXTIME, "most recent disk full", TOKU_ENGINE_STATUS);
+ FS_STATUS_INIT(FS_ENOSPC_COUNT, nullptr, UINT64, "number of write operations that returned ENOSPC", TOKU_ENGINE_STATUS);
+ FS_STATUS_INIT(FS_FSYNC_TIME, FILESYSTEM_FSYNC_TIME, UINT64, "fsync time", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ FS_STATUS_INIT(FS_FSYNC_COUNT, FILESYSTEM_FSYNC_NUM, UINT64, "fsync count", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ FS_STATUS_INIT(FS_LONG_FSYNC_TIME, FILESYSTEM_LONG_FSYNC_TIME, UINT64, "long fsync time", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ FS_STATUS_INIT(FS_LONG_FSYNC_COUNT, FILESYSTEM_LONG_FSYNC_NUM, UINT64, "long fsync count", TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS);
+ fsstat.initialized = true;
+}
+#undef FS_STATUS_INIT
+
+#define FS_STATUS_VALUE(x) fsstat.status[x].value.num
+
+static void
+fs_get_status(DB_ENV * env, fs_redzone_state * redzone_state) {
+ if (!fsstat.initialized)
+ fs_status_init();
+
+ time_t enospc_most_recent_timestamp;
+ uint64_t enospc_threads_blocked, enospc_total;
+ toku_fs_get_write_info(&enospc_most_recent_timestamp, &enospc_threads_blocked, &enospc_total);
+ if (enospc_threads_blocked)
+ FS_STATUS_VALUE(FS_ENOSPC_REDZONE_STATE) = FS_BLOCKED;
+ else
+ FS_STATUS_VALUE(FS_ENOSPC_REDZONE_STATE) = env->i->fs_state;
+ *redzone_state = (fs_redzone_state) FS_STATUS_VALUE(FS_ENOSPC_REDZONE_STATE);
+ FS_STATUS_VALUE(FS_ENOSPC_THREADS_BLOCKED) = enospc_threads_blocked;
+ FS_STATUS_VALUE(FS_ENOSPC_REDZONE_CTR) = env->i->enospc_redzone_ctr;
+ FS_STATUS_VALUE(FS_ENOSPC_MOST_RECENT) = enospc_most_recent_timestamp;
+ FS_STATUS_VALUE(FS_ENOSPC_COUNT) = enospc_total;
+
+ uint64_t fsync_count, fsync_time, long_fsync_threshold, long_fsync_count, long_fsync_time;
+ toku_get_fsync_times(&fsync_count, &fsync_time, &long_fsync_threshold, &long_fsync_count, &long_fsync_time);
+ FS_STATUS_VALUE(FS_FSYNC_COUNT) = fsync_count;
+ FS_STATUS_VALUE(FS_FSYNC_TIME) = fsync_time;
+ FS_STATUS_VALUE(FS_LONG_FSYNC_COUNT) = long_fsync_count;
+ FS_STATUS_VALUE(FS_LONG_FSYNC_TIME) = long_fsync_time;
+}
+#undef FS_STATUS_VALUE
+
+// Local status struct used to get information from memory.c
+typedef enum {
+ MEMORY_MALLOC_COUNT = 0,
+ MEMORY_FREE_COUNT,
+ MEMORY_REALLOC_COUNT,
+ MEMORY_MALLOC_FAIL,
+ MEMORY_REALLOC_FAIL,
+ MEMORY_REQUESTED,
+ MEMORY_USED,
+ MEMORY_FREED,
+ MEMORY_MAX_REQUESTED_SIZE,
+ MEMORY_LAST_FAILED_SIZE,
+ MEMORY_MAX_IN_USE,
+ MEMORY_MALLOCATOR_VERSION,
+ MEMORY_MMAP_THRESHOLD,
+ MEMORY_STATUS_NUM_ROWS
+} memory_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[MEMORY_STATUS_NUM_ROWS];
+} MEMORY_STATUS_S, *MEMORY_STATUS;
+
+static MEMORY_STATUS_S memory_status;
+
+#define STATUS_INIT(k,c,t,l) TOKUFT_STATUS_INIT(memory_status, k, c, t, "memory: " l, TOKU_ENGINE_STATUS|TOKU_GLOBAL_STATUS)
+
+static void
+memory_status_init(void) {
+ // Note, this function initializes the keyname, type, and legend fields.
+ // Value fields are initialized to zero by compiler.
+ STATUS_INIT(MEMORY_MALLOC_COUNT, MEMORY_MALLOC_COUNT, UINT64, "number of malloc operations");
+ STATUS_INIT(MEMORY_FREE_COUNT, MEMORY_FREE_COUNT, UINT64, "number of free operations");
+ STATUS_INIT(MEMORY_REALLOC_COUNT, MEMORY_REALLOC_COUNT, UINT64, "number of realloc operations");
+ STATUS_INIT(MEMORY_MALLOC_FAIL, MEMORY_MALLOC_FAIL, UINT64, "number of malloc operations that failed");
+ STATUS_INIT(MEMORY_REALLOC_FAIL, MEMORY_REALLOC_FAIL, UINT64, "number of realloc operations that failed" );
+ STATUS_INIT(MEMORY_REQUESTED, MEMORY_REQUESTED, UINT64, "number of bytes requested");
+ STATUS_INIT(MEMORY_USED, MEMORY_USED, UINT64, "number of bytes used (requested + overhead)");
+ STATUS_INIT(MEMORY_FREED, MEMORY_FREED, UINT64, "number of bytes freed");
+ STATUS_INIT(MEMORY_MAX_REQUESTED_SIZE, MEMORY_MAX_REQUESTED_SIZE, UINT64, "largest attempted allocation size");
+ STATUS_INIT(MEMORY_LAST_FAILED_SIZE, MEMORY_LAST_FAILED_SIZE, UINT64, "size of the last failed allocation attempt");
+ STATUS_INIT(MEMORY_MAX_IN_USE, MEM_ESTIMATED_MAXIMUM_MEMORY_FOOTPRINT, UINT64, "estimated maximum memory footprint");
+ STATUS_INIT(MEMORY_MALLOCATOR_VERSION, MEMORY_MALLOCATOR_VERSION, CHARSTR, "mallocator version");
+ STATUS_INIT(MEMORY_MMAP_THRESHOLD, MEMORY_MMAP_THRESHOLD, UINT64, "mmap threshold");
+ memory_status.initialized = true;
+}
+#undef STATUS_INIT
+
+#define MEMORY_STATUS_VALUE(x) memory_status.status[x].value.num
+
+static void
+memory_get_status(void) {
+ if (!memory_status.initialized)
+ memory_status_init();
+ LOCAL_MEMORY_STATUS_S local_memstat;
+ toku_memory_get_status(&local_memstat);
+ MEMORY_STATUS_VALUE(MEMORY_MALLOC_COUNT) = local_memstat.malloc_count;
+ MEMORY_STATUS_VALUE(MEMORY_FREE_COUNT) = local_memstat.free_count;
+ MEMORY_STATUS_VALUE(MEMORY_REALLOC_COUNT) = local_memstat.realloc_count;
+ MEMORY_STATUS_VALUE(MEMORY_MALLOC_FAIL) = local_memstat.malloc_fail;
+ MEMORY_STATUS_VALUE(MEMORY_REALLOC_FAIL) = local_memstat.realloc_fail;
+ MEMORY_STATUS_VALUE(MEMORY_REQUESTED) = local_memstat.requested;
+ MEMORY_STATUS_VALUE(MEMORY_USED) = local_memstat.used;
+ MEMORY_STATUS_VALUE(MEMORY_FREED) = local_memstat.freed;
+ MEMORY_STATUS_VALUE(MEMORY_MAX_IN_USE) = local_memstat.max_in_use;
+ MEMORY_STATUS_VALUE(MEMORY_MMAP_THRESHOLD) = local_memstat.mmap_threshold;
+ memory_status.status[MEMORY_MALLOCATOR_VERSION].value.str = local_memstat.mallocator_version;
+}
+#undef MEMORY_STATUS_VALUE
+
+// how many rows are in engine status?
+static int
+env_get_engine_status_num_rows (DB_ENV * UU(env), uint64_t * num_rowsp) {
+ uint64_t num_rows = 0;
+ num_rows += YDB_LAYER_STATUS_NUM_ROWS;
+ num_rows += YDB_C_LAYER_STATUS_NUM_ROWS;
+ num_rows += YDB_WRITE_LAYER_STATUS_NUM_ROWS;
+ num_rows += LE_STATUS_S::LE_STATUS_NUM_ROWS;
+ num_rows += CHECKPOINT_STATUS_S::CP_STATUS_NUM_ROWS;
+ num_rows += CACHETABLE_STATUS_S::CT_STATUS_NUM_ROWS;
+ num_rows += LTM_STATUS_S::LTM_STATUS_NUM_ROWS;
+ num_rows += FT_STATUS_S::FT_STATUS_NUM_ROWS;
+ num_rows += FT_FLUSHER_STATUS_S::FT_FLUSHER_STATUS_NUM_ROWS;
+ num_rows += FT_HOT_STATUS_S::FT_HOT_STATUS_NUM_ROWS;
+ num_rows += TXN_STATUS_S::TXN_STATUS_NUM_ROWS;
+ num_rows += LOGGER_STATUS_S::LOGGER_STATUS_NUM_ROWS;
+ num_rows += MEMORY_STATUS_NUM_ROWS;
+ num_rows += FS_STATUS_NUM_ROWS;
+ num_rows += INDEXER_STATUS_NUM_ROWS;
+ num_rows += LOADER_STATUS_NUM_ROWS;
+ num_rows += CTX_STATUS_NUM_ROWS;
+#if 0
+ // enable when upgrade is supported
+ num_rows += FT_UPGRADE_STATUS_NUM_ROWS;
+ num_rows += PERSISTENT_UPGRADE_STATUS_NUM_ROWS;
+#endif
+ *num_rowsp = num_rows;
+ return 0;
+}
+
+// Do not take ydb lock or any other lock around or in this function.
+// If the engine is blocked because some thread is holding a lock, this function
+// can help diagnose the problem.
+// This function only collects information, and it does not matter if something gets garbled
+// because of a race condition.
+// Note, engine status is still collected even if the environment or logger is panicked
+static int
+env_get_engine_status (DB_ENV * env, TOKU_ENGINE_STATUS_ROW engstat, uint64_t maxrows, uint64_t *num_rows, fs_redzone_state* redzone_state, uint64_t * env_panicp, char * env_panic_string_buf, int env_panic_string_length, toku_engine_status_include_type include_flags) {
+ int r;
+
+ if (env_panic_string_buf) {
+ if (env && env->i && env->i->is_panicked && env->i->panic_string) {
+ strncpy(env_panic_string_buf, env->i->panic_string, env_panic_string_length);
+ env_panic_string_buf[env_panic_string_length - 1] = '\0'; // just in case
+ }
+ else
+ *env_panic_string_buf = '\0';
+ }
+
+ if ( !(env) ||
+ !(env->i) ||
+ !(env_opened(env)) ||
+ !num_rows ||
+ !include_flags)
+ r = EINVAL;
+ else {
+ r = 0;
+ uint64_t row = 0; // which row to fill next
+ *env_panicp = env->i->is_panicked;
+
+ {
+ YDB_LAYER_STATUS_S ydb_stat;
+ ydb_layer_get_status(env, &ydb_stat);
+ for (int i = 0; i < YDB_LAYER_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (ydb_stat.status[i].include & include_flags) {
+ engstat[row++] = ydb_stat.status[i];
+ }
+ }
+ }
+ {
+ YDB_C_LAYER_STATUS_S ydb_c_stat;
+ ydb_c_layer_get_status(&ydb_c_stat);
+ for (int i = 0; i < YDB_C_LAYER_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (ydb_c_stat.status[i].include & include_flags) {
+ engstat[row++] = ydb_c_stat.status[i];
+ }
+ }
+ }
+ {
+ YDB_WRITE_LAYER_STATUS_S ydb_write_stat;
+ ydb_write_layer_get_status(&ydb_write_stat);
+ for (int i = 0; i < YDB_WRITE_LAYER_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (ydb_write_stat.status[i].include & include_flags) {
+ engstat[row++] = ydb_write_stat.status[i];
+ }
+ }
+ }
+ {
+ LE_STATUS_S lestat; // Rice's vampire
+ toku_le_get_status(&lestat);
+ for (int i = 0; i < LE_STATUS_S::LE_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (lestat.status[i].include & include_flags) {
+ engstat[row++] = lestat.status[i];
+ }
+ }
+ }
+ {
+ CHECKPOINT_STATUS_S cpstat;
+ toku_checkpoint_get_status(env->i->cachetable, &cpstat);
+ for (int i = 0; i < CHECKPOINT_STATUS_S::CP_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (cpstat.status[i].include & include_flags) {
+ engstat[row++] = cpstat.status[i];
+ }
+ }
+ }
+ {
+ CACHETABLE_STATUS_S ctstat;
+ toku_cachetable_get_status(env->i->cachetable, &ctstat);
+ for (int i = 0; i < CACHETABLE_STATUS_S::CT_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (ctstat.status[i].include & include_flags) {
+ engstat[row++] = ctstat.status[i];
+ }
+ }
+ }
+ {
+ LTM_STATUS_S ltmstat;
+ env->i->ltm.get_status(&ltmstat);
+ for (int i = 0; i < LTM_STATUS_S::LTM_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (ltmstat.status[i].include & include_flags) {
+ engstat[row++] = ltmstat.status[i];
+ }
+ }
+ }
+ {
+ FT_STATUS_S ftstat;
+ toku_ft_get_status(&ftstat);
+ for (int i = 0; i < FT_STATUS_S::FT_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (ftstat.status[i].include & include_flags) {
+ engstat[row++] = ftstat.status[i];
+ }
+ }
+ }
+ {
+ FT_FLUSHER_STATUS_S flusherstat;
+ toku_ft_flusher_get_status(&flusherstat);
+ for (int i = 0; i < FT_FLUSHER_STATUS_S::FT_FLUSHER_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (flusherstat.status[i].include & include_flags) {
+ engstat[row++] = flusherstat.status[i];
+ }
+ }
+ }
+ {
+ FT_HOT_STATUS_S hotstat;
+ toku_ft_hot_get_status(&hotstat);
+ for (int i = 0; i < FT_HOT_STATUS_S::FT_HOT_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (hotstat.status[i].include & include_flags) {
+ engstat[row++] = hotstat.status[i];
+ }
+ }
+ }
+ {
+ TXN_STATUS_S txnstat;
+ toku_txn_get_status(&txnstat);
+ for (int i = 0; i < TXN_STATUS_S::TXN_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (txnstat.status[i].include & include_flags) {
+ engstat[row++] = txnstat.status[i];
+ }
+ }
+ }
+ {
+ LOGGER_STATUS_S loggerstat;
+ toku_logger_get_status(env->i->logger, &loggerstat);
+ for (int i = 0; i < LOGGER_STATUS_S::LOGGER_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (loggerstat.status[i].include & include_flags) {
+ engstat[row++] = loggerstat.status[i];
+ }
+ }
+ }
+
+ {
+ INDEXER_STATUS_S indexerstat;
+ toku_indexer_get_status(&indexerstat);
+ for (int i = 0; i < INDEXER_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (indexerstat.status[i].include & include_flags) {
+ engstat[row++] = indexerstat.status[i];
+ }
+ }
+ }
+ {
+ LOADER_STATUS_S loaderstat;
+ toku_loader_get_status(&loaderstat);
+ for (int i = 0; i < LOADER_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (loaderstat.status[i].include & include_flags) {
+ engstat[row++] = loaderstat.status[i];
+ }
+ }
+ }
+
+ {
+ // memory_status is local to this file
+ memory_get_status();
+ for (int i = 0; i < MEMORY_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (memory_status.status[i].include & include_flags) {
+ engstat[row++] = memory_status.status[i];
+ }
+ }
+ }
+ {
+ // Note, fs_get_status() and the fsstat structure are local to this file because they
+ // are used to concentrate file system information collected from various places.
+ fs_get_status(env, redzone_state);
+ for (int i = 0; i < FS_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (fsstat.status[i].include & include_flags) {
+ engstat[row++] = fsstat.status[i];
+ }
+ }
+ }
+ {
+ struct context_status ctxstatus;
+ toku_context_get_status(&ctxstatus);
+ for (int i = 0; i < CTX_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (ctxstatus.status[i].include & include_flags) {
+ engstat[row++] = ctxstatus.status[i];
+ }
+ }
+ }
+#if 0
+ // enable when upgrade is supported
+ {
+ for (int i = 0; i < PERSISTENT_UPGRADE_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (persistent_upgrade_status.status[i].include & include_flags) {
+ engstat[row++] = persistent_upgrade_status.status[i];
+ }
+ }
+ FT_UPGRADE_STATUS_S ft_upgradestat;
+ toku_ft_upgrade_get_status(&ft_upgradestat);
+ for (int i = 0; i < FT_UPGRADE_STATUS_NUM_ROWS && row < maxrows; i++) {
+ if (ft_upgradestat.status[i].include & include_flags) {
+ engstat[row++] = ft_upgradestat.status[i];
+ }
+ }
+
+ }
+#endif
+ if (r==0) {
+ *num_rows = row;
+ }
+ }
+ return r;
+}
+
+// Fill buff with text description of engine status up to bufsiz bytes.
+// Intended for use by test programs that do not have the handlerton available,
+// and for use by toku_assert logic to print diagnostic info on crash.
+static int
+env_get_engine_status_text(DB_ENV * env, char * buff, int bufsiz) {
+ uint32_t stringsize = 1024;
+ uint64_t panic;
+ char panicstring[stringsize];
+ int n = 0; // number of characters printed so far
+ uint64_t num_rows;
+ uint64_t max_rows;
+ fs_redzone_state redzone_state;
+
+ n = snprintf(buff, bufsiz - n, "BUILD_ID = %d\n", BUILD_ID);
+
+ (void) env_get_engine_status_num_rows (env, &max_rows);
+ TOKU_ENGINE_STATUS_ROW_S mystat[max_rows];
+ int r = env->get_engine_status (env, mystat, max_rows, &num_rows, &redzone_state, &panic, panicstring, stringsize, TOKU_ENGINE_STATUS);
+
+ if (r) {
+ n += snprintf(buff + n, bufsiz - n, "Engine status not available: ");
+ if (!env) {
+ n += snprintf(buff + n, bufsiz - n, "no environment\n");
+ }
+ else if (!(env->i)) {
+ n += snprintf(buff + n, bufsiz - n, "environment internal struct is null\n");
+ }
+ else if (!env_opened(env)) {
+ n += snprintf(buff + n, bufsiz - n, "environment is not open\n");
+ }
+ }
+ else {
+ if (panic) {
+ n += snprintf(buff + n, bufsiz - n, "Env panic code: %" PRIu64 "\n", panic);
+ if (strlen(panicstring)) {
+ invariant(strlen(panicstring) <= stringsize);
+ n += snprintf(buff + n, bufsiz - n, "Env panic string: %s\n", panicstring);
+ }
+ }
+
+ for (uint64_t row = 0; row < num_rows; row++) {
+ n += snprintf(buff + n, bufsiz - n, "%s: ", mystat[row].legend);
+ switch (mystat[row].type) {
+ case FS_STATE:
+ n += snprintf(buff + n, bufsiz - n, "%" PRIu64 "\n", mystat[row].value.num);
+ break;
+ case UINT64:
+ n += snprintf(buff + n, bufsiz - n, "%" PRIu64 "\n", mystat[row].value.num);
+ break;
+ case CHARSTR:
+ n += snprintf(buff + n, bufsiz - n, "%s\n", mystat[row].value.str);
+ break;
+ case UNIXTIME:
+ {
+ char tbuf[26];
+ format_time((time_t*)&mystat[row].value.num, tbuf);
+ n += snprintf(buff + n, bufsiz - n, "%s\n", tbuf);
+ }
+ break;
+ case TOKUTIME:
+ {
+ double t = tokutime_to_seconds(mystat[row].value.num);
+ n += snprintf(buff + n, bufsiz - n, "%.6f\n", t);
+ }
+ break;
+ case PARCOUNT:
+ {
+ uint64_t v = read_partitioned_counter(mystat[row].value.parcount);
+ n += snprintf(buff + n, bufsiz - n, "%" PRIu64 "\n", v);
+ }
+ break;
+ default:
+ n += snprintf(buff + n, bufsiz - n, "UNKNOWN STATUS TYPE: %d\n", mystat[row].type);
+ break;
+ }
+ }
+ }
+
+ if (n > bufsiz) {
+ const char * errmsg = "BUFFER TOO SMALL\n";
+ int len = strlen(errmsg) + 1;
+ (void) snprintf(buff + (bufsiz - 1) - len, len, "%s", errmsg);
+ }
+
+ return r;
+}
+
+// prints engine status using toku_env_err line-by-line
+static int
+env_err_engine_status(DB_ENV * env) {
+ uint32_t stringsize = 1024;
+ uint64_t panic;
+ char panicstring[stringsize];
+ uint64_t num_rows;
+ uint64_t max_rows;
+ fs_redzone_state redzone_state;
+
+ toku_env_err(env, 0, "BUILD_ID = %d", BUILD_ID);
+
+ (void) env_get_engine_status_num_rows (env, &max_rows);
+ TOKU_ENGINE_STATUS_ROW_S mystat[max_rows];
+ int r = env->get_engine_status (env, mystat, max_rows, &num_rows, &redzone_state, &panic, panicstring, stringsize, TOKU_ENGINE_STATUS);
+
+ if (r) {
+ toku_env_err(env, 0, "Engine status not available: ");
+ if (!env) {
+ toku_env_err(env, 0, "no environment");
+ }
+ else if (!(env->i)) {
+ toku_env_err(env, 0, "environment internal struct is null");
+ }
+ else if (!env_opened(env)) {
+ toku_env_err(env, 0, "environment is not open");
+ }
+ }
+ else {
+ if (panic) {
+ toku_env_err(env, 0, "Env panic code: %" PRIu64, panic);
+ if (strlen(panicstring)) {
+ invariant(strlen(panicstring) <= stringsize);
+ toku_env_err(env, 0, "Env panic string: %s", panicstring);
+ }
+ }
+
+ for (uint64_t row = 0; row < num_rows; row++) {
+ switch (mystat[row].type) {
+ case FS_STATE:
+ toku_env_err(env, 0, "%s: %" PRIu64, mystat[row].legend, mystat[row].value.num);
+ break;
+ case UINT64:
+ toku_env_err(env, 0, "%s: %" PRIu64, mystat[row].legend, mystat[row].value.num);
+ break;
+ case CHARSTR:
+ toku_env_err(env, 0, "%s: %s", mystat[row].legend, mystat[row].value.str);
+ break;
+ case UNIXTIME:
+ {
+ char tbuf[26];
+ format_time((time_t*)&mystat[row].value.num, tbuf);
+ toku_env_err(env, 0, "%s: %s", mystat[row].legend, tbuf);
+ }
+ break;
+ case TOKUTIME:
+ {
+ double t = tokutime_to_seconds(mystat[row].value.num);
+ toku_env_err(env, 0, "%s: %.6f", mystat[row].legend, t);
+ }
+ break;
+ case PARCOUNT:
+ {
+ uint64_t v = read_partitioned_counter(mystat[row].value.parcount);
+ toku_env_err(env, 0, "%s: %" PRIu64, mystat[row].legend, v);
+ }
+ break;
+ default:
+ toku_env_err(env, 0, "%s: UNKNOWN STATUS TYPE: %d", mystat[row].legend, mystat[row].type);
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+// intended for use by toku_assert logic, when env is not known
+static int
+toku_maybe_get_engine_status_text (char * buff, int buffsize) {
+ DB_ENV * env = most_recent_env;
+ int r;
+ if (engine_status_enable && env != NULL) {
+ r = env_get_engine_status_text(env, buff, buffsize);
+ }
+ else {
+ r = EOPNOTSUPP;
+ snprintf(buff, buffsize, "Engine status not available: disabled by user. This should only happen in test programs.\n");
+ }
+ return r;
+}
+
+static int
+toku_maybe_err_engine_status (void) {
+ DB_ENV * env = most_recent_env;
+ int r;
+ if (engine_status_enable && env != NULL) {
+ r = env_err_engine_status(env);
+ }
+ else {
+ r = EOPNOTSUPP;
+ }
+ return r;
+}
+
+// Set panic code and panic string if not already panicked,
+// intended for use by toku_assert when about to abort().
+static void
+toku_maybe_set_env_panic(int code, const char * msg) {
+ if (code == 0)
+ code = -1;
+ if (msg == NULL)
+ msg = "Unknown cause from abort (failed assert)\n";
+ env_is_panicked = code; // disable library destructor no matter what
+ DB_ENV * env = most_recent_env;
+ if (env &&
+ env->i &&
+ (env->i->is_panicked == 0)) {
+ env_panic(env, code, msg);
+ }
+}
+
+// handlerton's call to fractal tree layer on failed assert in handlerton
+static int
+env_crash(DB_ENV * UU(db_env), const char* msg, const char * fun, const char* file, int line, int caller_errno) {
+ toku_do_assert_fail(msg, fun, file, line, caller_errno);
+ return -1; // placate compiler
+}
+
+static int
+env_get_cursor_for_persistent_environment(DB_ENV* env, DB_TXN* txn, DBC** c) {
+ if (!env_opened(env)) {
+ return EINVAL;
+ }
+ return toku_db_cursor(env->i->persistent_environment, txn, c, 0);
+}
+
+static int
+env_get_cursor_for_directory(DB_ENV* env, DB_TXN* txn, DBC** c) {
+ if (!env_opened(env)) {
+ return EINVAL;
+ }
+ return toku_db_cursor(env->i->directory, txn, c, 0);
+}
+
+static DB *
+env_get_db_for_directory(DB_ENV* env) {
+ if (!env_opened(env)) {
+ return NULL;
+ }
+ return env->i->directory;
+}
+
+struct ltm_iterate_requests_callback_extra {
+ ltm_iterate_requests_callback_extra(DB_ENV *e,
+ iterate_requests_callback cb,
+ void *ex) :
+ env(e), callback(cb), extra(ex) {
+ }
+ DB_ENV *env;
+ iterate_requests_callback callback;
+ void *extra;
+};
+
+static int
+find_db_by_dict_id(DB *const &db, const DICTIONARY_ID &dict_id_find) {
+ DICTIONARY_ID dict_id = db->i->dict_id;
+ if (dict_id.dictid < dict_id_find.dictid) {
+ return -1;
+ } else if (dict_id.dictid > dict_id_find.dictid) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static DB *
+locked_get_db_by_dict_id(DB_ENV *env, DICTIONARY_ID dict_id) {
+ DB *db;
+ int r = env->i->open_dbs_by_dict_id->find_zero<DICTIONARY_ID, find_db_by_dict_id>(dict_id, &db, nullptr);
+ return r == 0 ? db : nullptr;
+}
+
+static int ltm_iterate_requests_callback(DICTIONARY_ID dict_id, TXNID txnid,
+ const DBT *left_key,
+ const DBT *right_key,
+ TXNID blocking_txnid,
+ uint64_t start_time,
+ void *extra) {
+ ltm_iterate_requests_callback_extra *info =
+ reinterpret_cast<ltm_iterate_requests_callback_extra *>(extra);
+
+ toku_pthread_rwlock_rdlock(&info->env->i->open_dbs_rwlock);
+ int r = 0;
+ DB *db = locked_get_db_by_dict_id(info->env, dict_id);
+ if (db != nullptr) {
+ r = info->callback(db, txnid, left_key, right_key,
+ blocking_txnid, start_time, info->extra);
+ }
+ toku_pthread_rwlock_rdunlock(&info->env->i->open_dbs_rwlock);
+ return r;
+}
+
+static int
+env_iterate_pending_lock_requests(DB_ENV *env,
+ iterate_requests_callback callback,
+ void *extra) {
+ if (!env_opened(env)) {
+ return EINVAL;
+ }
+
+ toku::locktree_manager *mgr = &env->i->ltm;
+ ltm_iterate_requests_callback_extra e(env, callback, extra);
+ return mgr->iterate_pending_lock_requests(ltm_iterate_requests_callback, &e);
+}
+
+// for the lifetime of this object:
+// - open_dbs_rwlock must be read locked (or better)
+// - txn_mutex must be held
+struct iter_txn_row_locks_callback_extra {
+ iter_txn_row_locks_callback_extra(DB_ENV *e, toku::omt<txn_lt_key_ranges> *m) :
+ env(e), current_db(nullptr), which_lt(0), lt_map(m) {
+ if (lt_map->size() > 0) {
+ set_iterator_and_current_db();
+ }
+ }
+
+ void set_iterator_and_current_db() {
+ txn_lt_key_ranges ranges;
+ const int r = lt_map->fetch(which_lt, &ranges);
+ invariant_zero(r);
+ current_db = locked_get_db_by_dict_id(env, ranges.lt->get_dict_id());
+ iter = toku::range_buffer::iterator(ranges.buffer);
+ }
+
+ DB_ENV *env;
+ DB *current_db;
+ size_t which_lt;
+ toku::omt<txn_lt_key_ranges> *lt_map;
+ toku::range_buffer::iterator iter;
+ toku::range_buffer::iterator::record rec;
+};
+
+static int iter_txn_row_locks_callback(DB **db, DBT *left_key, DBT *right_key, void *extra) {
+ iter_txn_row_locks_callback_extra *info =
+ reinterpret_cast<iter_txn_row_locks_callback_extra *>(extra);
+
+ while (info->which_lt < info->lt_map->size()) {
+ const bool more = info->iter.current(&info->rec);
+ if (more) {
+ *db = info->current_db;
+ // The caller should interpret data/size == 0 to mean infinity.
+ // Therefore, when we copyref pos/neg infinity into left/right_key,
+ // the caller knows what we're talking about.
+ toku_copyref_dbt(left_key, *info->rec.get_left_key());
+ toku_copyref_dbt(right_key, *info->rec.get_right_key());
+ info->iter.next();
+ return 0;
+ } else {
+ info->which_lt++;
+ if (info->which_lt < info->lt_map->size()) {
+ info->set_iterator_and_current_db();
+ }
+ }
+ }
+ return DB_NOTFOUND;
+}
+
+struct iter_txns_callback_extra {
+ iter_txns_callback_extra(DB_ENV *e, iterate_transactions_callback cb, void *ex) :
+ env(e), callback(cb), extra(ex) {
+ }
+ DB_ENV *env;
+ iterate_transactions_callback callback;
+ void *extra;
+};
+
+static int iter_txns_callback(TOKUTXN txn, void *extra) {
+ int r = 0;
+ iter_txns_callback_extra *info =
+ reinterpret_cast<iter_txns_callback_extra *>(extra);
+ DB_TXN *dbtxn = toku_txn_get_container_db_txn(txn);
+ invariant_notnull(dbtxn);
+ struct __toku_db_txn_internal *db_txn_internal __attribute__((__unused__)) = db_txn_struct_i(dbtxn);
+ TOKU_VALGRIND_HG_DISABLE_CHECKING(db_txn_internal, sizeof *db_txn_internal);
+ if (db_txn_struct_i(dbtxn)->tokutxn == txn) { // make sure that the dbtxn is fully initialized
+ toku_mutex_lock(&db_txn_struct_i(dbtxn)->txn_mutex);
+ toku_pthread_rwlock_rdlock(&info->env->i->open_dbs_rwlock);
+
+ iter_txn_row_locks_callback_extra e(info->env, &db_txn_struct_i(dbtxn)->lt_map);
+ r = info->callback(dbtxn, iter_txn_row_locks_callback, &e, info->extra);
+
+ toku_pthread_rwlock_rdunlock(&info->env->i->open_dbs_rwlock);
+ toku_mutex_unlock(&db_txn_struct_i(dbtxn)->txn_mutex);
+ }
+ TOKU_VALGRIND_HG_ENABLE_CHECKING(db_txn_internal, sizeof *db_txn_internal);
+
+ return r;
+}
+
+static int
+env_iterate_live_transactions(DB_ENV *env,
+ iterate_transactions_callback callback,
+ void *extra) {
+ if (!env_opened(env)) {
+ return EINVAL;
+ }
+
+ TXN_MANAGER txn_manager = toku_logger_get_txn_manager(env->i->logger);
+ iter_txns_callback_extra e(env, callback, extra);
+ return toku_txn_manager_iter_over_live_root_txns(txn_manager, iter_txns_callback, &e);
+}
+
+static void env_set_loader_memory_size(DB_ENV *env, uint64_t (*get_loader_memory_size_callback)(void)) {
+ env->i->get_loader_memory_size_callback = get_loader_memory_size_callback;
+}
+
+static uint64_t env_get_loader_memory_size(DB_ENV *env) {
+ uint64_t memory_size = 0;
+ if (env->i->get_loader_memory_size_callback)
+ memory_size = env->i->get_loader_memory_size_callback();
+ return memory_size;
+}
+
+static void env_set_killed_callback(DB_ENV *env, uint64_t default_killed_time_msec, uint64_t (*get_killed_time_callback)(uint64_t default_killed_time_msec), int (*killed_callback)(void)) {
+ env->i->default_killed_time_msec = default_killed_time_msec;
+ env->i->get_killed_time_callback = get_killed_time_callback;
+ env->i->killed_callback = killed_callback;
+}
+
+static void env_do_backtrace(DB_ENV *env) {
+ if (env->i->errcall) {
+ db_env_do_backtrace_errfunc((toku_env_err_func) toku_env_err, (const void *) env);
+ }
+ if (env->i->errfile) {
+ db_env_do_backtrace((FILE *) env->i->errfile);
+ } else {
+ db_env_do_backtrace(stderr);
+ }
+}
+
+static int
+toku_env_create(DB_ENV ** envp, uint32_t flags) {
+ int r = ENOSYS;
+ DB_ENV* result = NULL;
+
+ if (flags!=0) { r = EINVAL; goto cleanup; }
+ MALLOC(result);
+ if (result == 0) { r = ENOMEM; goto cleanup; }
+ memset(result, 0, sizeof *result);
+
+ // locked methods
+ result->err = (void (*)(const DB_ENV * env, int error, const char *fmt, ...)) toku_env_err;
+#define SENV(name) result->name = locked_env_ ## name
+ SENV(dbremove);
+ SENV(dbrename);
+ //SENV(set_noticecall);
+#undef SENV
+#define USENV(name) result->name = env_ ## name
+ // methods with locking done internally
+ USENV(put_multiple);
+ USENV(del_multiple);
+ USENV(update_multiple);
+ // unlocked methods
+ USENV(open);
+ USENV(close);
+ USENV(set_default_bt_compare);
+ USENV(set_update);
+ USENV(set_generate_row_callback_for_put);
+ USENV(set_generate_row_callback_for_del);
+ USENV(set_lg_bsize);
+ USENV(set_lg_dir);
+ USENV(set_lg_max);
+ USENV(get_lg_max);
+ USENV(set_lk_max_memory);
+ USENV(get_lk_max_memory);
+ USENV(get_iname);
+ USENV(set_errcall);
+ USENV(set_errfile);
+ USENV(set_errpfx);
+ USENV(set_data_dir);
+ USENV(checkpointing_set_period);
+ USENV(checkpointing_get_period);
+ USENV(cleaner_set_period);
+ USENV(cleaner_get_period);
+ USENV(cleaner_set_iterations);
+ USENV(cleaner_get_iterations);
+ USENV(evictor_set_enable_partial_eviction);
+ USENV(evictor_get_enable_partial_eviction);
+ USENV(set_cachesize);
+ USENV(set_client_pool_threads);
+ USENV(set_cachetable_pool_threads);
+ USENV(set_checkpoint_pool_threads);
+#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
+ USENV(get_cachesize);
+#endif
+#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR <= 4
+ USENV(set_lk_max);
+#endif
+ USENV(set_lk_detect);
+ USENV(set_flags);
+ USENV(set_tmp_dir);
+ USENV(set_verbose);
+ USENV(txn_recover);
+ USENV(txn_xa_recover);
+ USENV(get_txn_from_xid);
+ USENV(txn_stat);
+ USENV(get_lock_timeout);
+ USENV(set_lock_timeout);
+ USENV(set_lock_timeout_callback);
+ USENV(set_redzone);
+ USENV(log_flush);
+ USENV(log_archive);
+ USENV(create_loader);
+ USENV(get_cursor_for_persistent_environment);
+ USENV(get_cursor_for_directory);
+ USENV(get_db_for_directory);
+ USENV(iterate_pending_lock_requests);
+ USENV(iterate_live_transactions);
+ USENV(change_fsync_log_period);
+ USENV(set_loader_memory_size);
+ USENV(get_loader_memory_size);
+ USENV(set_killed_callback);
+ USENV(do_backtrace);
+#undef USENV
+
+ // unlocked methods
+ result->create_indexer = toku_indexer_create_indexer;
+ result->txn_checkpoint = toku_env_txn_checkpoint;
+ result->checkpointing_postpone = env_checkpointing_postpone;
+ result->checkpointing_resume = env_checkpointing_resume;
+ result->checkpointing_begin_atomic_operation = env_checkpointing_begin_atomic_operation;
+ result->checkpointing_end_atomic_operation = env_checkpointing_end_atomic_operation;
+ result->get_engine_status_num_rows = env_get_engine_status_num_rows;
+ result->get_engine_status = env_get_engine_status;
+ result->get_engine_status_text = env_get_engine_status_text;
+ result->crash = env_crash; // handlerton's call to fractal tree layer on failed assert
+ result->txn_begin = toku_txn_begin;
+
+ MALLOC(result->i);
+ if (result->i == 0) { r = ENOMEM; goto cleanup; }
+ memset(result->i, 0, sizeof *result->i);
+ result->i->envdir_lockfd = -1;
+ result->i->datadir_lockfd = -1;
+ result->i->logdir_lockfd = -1;
+ result->i->tmpdir_lockfd = -1;
+ env_fs_init(result);
+ env_fsync_log_init(result);
+
+ result->i->bt_compare = toku_builtin_compare_fun;
+
+ r = toku_logger_create(&result->i->logger);
+ invariant_zero(r);
+ invariant_notnull(result->i->logger);
+
+ // Create the locktree manager, passing in the create/destroy/escalate callbacks.
+ // The extra parameter for escalation is simply a pointer to this environment.
+ // The escalate callback will need it to translate txnids to DB_TXNs
+ result->i->ltm.create(toku_db_lt_on_create_callback, toku_db_lt_on_destroy_callback, toku_db_txn_escalate_callback, result);
+
+ XMALLOC(result->i->open_dbs_by_dname);
+ result->i->open_dbs_by_dname->create();
+ XMALLOC(result->i->open_dbs_by_dict_id);
+ result->i->open_dbs_by_dict_id->create();
+ toku_pthread_rwlock_init(&result->i->open_dbs_rwlock, NULL);
+
+ *envp = result;
+ r = 0;
+ toku_sync_fetch_and_add(&tokuft_num_envs, 1);
+cleanup:
+ if (r!=0) {
+ if (result) {
+ toku_free(result->i);
+ toku_free(result);
+ }
+ }
+ return r;
+}
+
+int
+DB_ENV_CREATE_FUN (DB_ENV ** envp, uint32_t flags) {
+ int r = toku_env_create(envp, flags);
+ return r;
+}
+
+// return 0 if v and dbv refer to same db (including same dname)
+// return <0 if v is earlier in omt than dbv
+// return >0 if v is later in omt than dbv
+static int
+find_db_by_db_dname(DB *const &db, DB *const &dbfind) {
+ int cmp;
+ const char *dname = db->i->dname;
+ const char *dnamefind = dbfind->i->dname;
+ cmp = strcmp(dname, dnamefind);
+ if (cmp != 0) return cmp;
+ if (db < dbfind) return -1;
+ if (db > dbfind) return 1;
+ return 0;
+}
+
+static int
+find_db_by_db_dict_id(DB *const &db, DB *const &dbfind) {
+ DICTIONARY_ID dict_id = db->i->dict_id;
+ DICTIONARY_ID dict_id_find = dbfind->i->dict_id;
+ if (dict_id.dictid < dict_id_find.dictid) {
+ return -1;
+ } else if (dict_id.dictid > dict_id_find.dictid) {
+ return 1;
+ } else if (db < dbfind) {
+ return -1;
+ } else if (db > dbfind) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+// Tell env that there is a new db handle (with non-unique dname in db->i-dname)
+void
+env_note_db_opened(DB_ENV *env, DB *db) {
+ toku_pthread_rwlock_wrlock(&env->i->open_dbs_rwlock);
+ assert(db->i->dname); // internal (non-user) dictionary has no dname
+
+ int r;
+ uint32_t idx;
+
+ r = env->i->open_dbs_by_dname->find_zero<DB *, find_db_by_db_dname>(db, nullptr, &idx);
+ assert(r == DB_NOTFOUND);
+ r = env->i->open_dbs_by_dname->insert_at(db, idx);
+ assert_zero(r);
+ r = env->i->open_dbs_by_dict_id->find_zero<DB *, find_db_by_db_dict_id>(db, nullptr, &idx);
+ assert(r == DB_NOTFOUND);
+ r = env->i->open_dbs_by_dict_id->insert_at(db, idx);
+ assert_zero(r);
+
+ STATUS_VALUE(YDB_LAYER_NUM_OPEN_DBS) = env->i->open_dbs_by_dname->size();
+ STATUS_VALUE(YDB_LAYER_NUM_DB_OPEN)++;
+ if (STATUS_VALUE(YDB_LAYER_NUM_OPEN_DBS) > STATUS_VALUE(YDB_LAYER_MAX_OPEN_DBS)) {
+ STATUS_VALUE(YDB_LAYER_MAX_OPEN_DBS) = STATUS_VALUE(YDB_LAYER_NUM_OPEN_DBS);
+ }
+ toku_pthread_rwlock_wrunlock(&env->i->open_dbs_rwlock);
+}
+
+// Effect: Tell the DB_ENV that the DB is no longer in use by the user of the API. The DB may still be in use by the fractal tree internals.
+void
+env_note_db_closed(DB_ENV *env, DB *db) {
+ toku_pthread_rwlock_wrlock(&env->i->open_dbs_rwlock);
+ assert(db->i->dname); // internal (non-user) dictionary has no dname
+ assert(env->i->open_dbs_by_dname->size() > 0);
+ assert(env->i->open_dbs_by_dict_id->size() > 0);
+
+ int r;
+ uint32_t idx;
+
+ r = env->i->open_dbs_by_dname->find_zero<DB *, find_db_by_db_dname>(db, nullptr, &idx);
+ assert_zero(r);
+ r = env->i->open_dbs_by_dname->delete_at(idx);
+ assert_zero(r);
+ r = env->i->open_dbs_by_dict_id->find_zero<DB *, find_db_by_db_dict_id>(db, nullptr, &idx);
+ assert_zero(r);
+ r = env->i->open_dbs_by_dict_id->delete_at(idx);
+ assert_zero(r);
+
+ STATUS_VALUE(YDB_LAYER_NUM_DB_CLOSE)++;
+ STATUS_VALUE(YDB_LAYER_NUM_OPEN_DBS) = env->i->open_dbs_by_dname->size();
+ toku_pthread_rwlock_wrunlock(&env->i->open_dbs_rwlock);
+}
+
+static int
+find_open_db_by_dname(DB *const &db, const char *const &dnamefind) {
+ return strcmp(db->i->dname, dnamefind);
+}
+
+// return true if there is any db open with the given dname
+static bool
+env_is_db_with_dname_open(DB_ENV *env, const char *dname) {
+ DB *db;
+ toku_pthread_rwlock_rdlock(&env->i->open_dbs_rwlock);
+ int r = env->i->open_dbs_by_dname->find_zero<const char *, find_open_db_by_dname>(dname, &db, nullptr);
+ if (r == 0) {
+ invariant(strcmp(dname, db->i->dname) == 0);
+ } else {
+ invariant(r == DB_NOTFOUND);
+ }
+ toku_pthread_rwlock_rdunlock(&env->i->open_dbs_rwlock);
+ return r == 0 ? true : false;
+}
+
+//We do not (yet?) support deleting subdbs by deleting the enclosing 'fname'
+static int
+env_dbremove_subdb(DB_ENV * env, DB_TXN * txn, const char *fname, const char *dbname, int32_t flags) {
+ int r;
+ if (!fname || !dbname) r = EINVAL;
+ else {
+ char subdb_full_name[strlen(fname) + sizeof("/") + strlen(dbname)];
+ int bytes = snprintf(subdb_full_name, sizeof(subdb_full_name), "%s/%s", fname, dbname);
+ assert(bytes==(int)sizeof(subdb_full_name)-1);
+ const char *null_subdbname = NULL;
+ r = env_dbremove(env, txn, subdb_full_name, null_subdbname, flags);
+ }
+ return r;
+}
+
+// see if we can acquire a table lock for the given dname.
+// requires: write lock on dname in the directory. dictionary
+// open, close, and begin checkpoint cannot occur.
+// returns: true if we could open, lock, and close a dictionary
+// with the given dname, false otherwise.
+static bool
+can_acquire_table_lock(DB_ENV *env, DB_TXN *txn, const char *iname_in_env) {
+ int r;
+ bool got_lock = false;
+ DB *db;
+
+ r = toku_db_create(&db, env, 0);
+ assert_zero(r);
+ r = toku_db_open_iname(db, txn, iname_in_env, 0, 0);
+ assert_zero(r);
+ r = toku_db_pre_acquire_table_lock(db, txn);
+ if (r == 0) {
+ got_lock = true;
+ } else {
+ got_lock = false;
+ }
+ r = toku_db_close(db);
+ assert_zero(r);
+
+ return got_lock;
+}
+
+static int
+env_dbremove(DB_ENV * env, DB_TXN *txn, const char *fname, const char *dbname, uint32_t flags) {
+ int r;
+ HANDLE_PANICKED_ENV(env);
+ if (!env_opened(env) || flags != 0) {
+ return EINVAL;
+ }
+ HANDLE_READ_ONLY_TXN(txn);
+ if (dbname != NULL) {
+ // env_dbremove_subdb() converts (fname, dbname) to dname
+ return env_dbremove_subdb(env, txn, fname, dbname, flags);
+ }
+
+ const char * dname = fname;
+ assert(dbname == NULL);
+
+ // We check for an open db here as a "fast path" to error.
+ // We'll need to check again below to be sure.
+ if (env_is_db_with_dname_open(env, dname)) {
+ return toku_ydb_do_error(env, EINVAL, "Cannot remove dictionary with an open handle.\n");
+ }
+
+ DBT dname_dbt;
+ DBT iname_dbt;
+ toku_fill_dbt(&dname_dbt, dname, strlen(dname)+1);
+ toku_init_dbt_flags(&iname_dbt, DB_DBT_REALLOC);
+
+ // get iname
+ r = toku_db_get(env->i->directory, txn, &dname_dbt, &iname_dbt, DB_SERIALIZABLE); // allocates memory for iname
+ char *iname = (char *) iname_dbt.data;
+ DB *db = NULL;
+ if (r != 0) {
+ if (r == DB_NOTFOUND) {
+ r = ENOENT;
+ }
+ goto exit;
+ }
+ // remove (dname,iname) from directory
+ r = toku_db_del(env->i->directory, txn, &dname_dbt, DB_DELETE_ANY, true);
+ if (r != 0) {
+ goto exit;
+ }
+ r = toku_db_create(&db, env, 0);
+ lazy_assert_zero(r);
+ r = toku_db_open_iname(db, txn, iname, 0, 0);
+ if (txn && r) {
+ if (r == EMFILE || r == ENFILE)
+ r = toku_ydb_do_error(env, r, "toku dbremove failed because open file limit reached\n");
+ else
+ r = toku_ydb_do_error(env, r, "toku dbremove failed\n");
+ goto exit;
+ }
+ if (txn) {
+ // Now that we have a writelock on dname, verify that there are still no handles open. (to prevent race conditions)
+ if (env_is_db_with_dname_open(env, dname)) {
+ r = toku_ydb_do_error(env, EINVAL, "Cannot remove dictionary with an open handle.\n");
+ goto exit;
+ }
+ // we know a live db handle does not exist.
+ //
+ // use the internally opened db to try and get a table lock
+ //
+ // if we can't get it, then some txn needs the ft and we
+ // should return lock not granted.
+ //
+ // otherwise, we're okay in marking this ft as remove on
+ // commit. no new handles can open for this dictionary
+ // because the txn has directory write locks on the dname
+ r = toku_db_pre_acquire_table_lock(db, txn);
+ if (r != 0) {
+ r = DB_LOCK_NOTGRANTED;
+ goto exit;
+ }
+ // The ft will be unlinked when the txn commits
+ toku_ft_unlink_on_commit(db->i->ft_handle, db_txn_struct_i(txn)->tokutxn);
+ }
+ else {
+ // unlink the ft without a txn
+ toku_ft_unlink(db->i->ft_handle);
+ }
+
+exit:
+ if (db) {
+ int ret = toku_db_close(db);
+ assert(ret == 0);
+ }
+ if (iname) {
+ toku_free(iname);
+ }
+ return r;
+}
+
+static int
+env_dbrename_subdb(DB_ENV *env, DB_TXN *txn, const char *fname, const char *dbname, const char *newname, uint32_t flags) {
+ int r;
+ if (!fname || !dbname || !newname) r = EINVAL;
+ else {
+ char subdb_full_name[strlen(fname) + sizeof("/") + strlen(dbname)];
+ {
+ int bytes = snprintf(subdb_full_name, sizeof(subdb_full_name), "%s/%s", fname, dbname);
+ assert(bytes==(int)sizeof(subdb_full_name)-1);
+ }
+ char new_full_name[strlen(fname) + sizeof("/") + strlen(dbname)];
+ {
+ int bytes = snprintf(new_full_name, sizeof(new_full_name), "%s/%s", fname, dbname);
+ assert(bytes==(int)sizeof(new_full_name)-1);
+ }
+ const char *null_subdbname = NULL;
+ r = env_dbrename(env, txn, subdb_full_name, null_subdbname, new_full_name, flags);
+ }
+ return r;
+}
+
+static int
+env_dbrename(DB_ENV *env, DB_TXN *txn, const char *fname, const char *dbname, const char *newname, uint32_t flags) {
+ int r;
+ HANDLE_PANICKED_ENV(env);
+ if (!env_opened(env) || flags != 0) {
+ return EINVAL;
+ }
+ HANDLE_READ_ONLY_TXN(txn);
+ if (dbname != NULL) {
+ // env_dbrename_subdb() converts (fname, dbname) to dname and (fname, newname) to newdname
+ return env_dbrename_subdb(env, txn, fname, dbname, newname, flags);
+ }
+
+ const char * dname = fname;
+ assert(dbname == NULL);
+
+ // We check for open dnames for the old and new name as a "fast path" to error.
+ // We will need to check these again later.
+ if (env_is_db_with_dname_open(env, dname)) {
+ return toku_ydb_do_error(env, EINVAL, "Cannot rename dictionary with an open handle.\n");
+ }
+ if (env_is_db_with_dname_open(env, newname)) {
+ return toku_ydb_do_error(env, EINVAL, "Cannot rename dictionary; Dictionary with target name has an open handle.\n");
+ }
+
+ DBT old_dname_dbt;
+ DBT new_dname_dbt;
+ DBT iname_dbt;
+ toku_fill_dbt(&old_dname_dbt, dname, strlen(dname)+1);
+ toku_fill_dbt(&new_dname_dbt, newname, strlen(newname)+1);
+ toku_init_dbt_flags(&iname_dbt, DB_DBT_REALLOC);
+
+ // get iname
+ r = toku_db_get(env->i->directory, txn, &old_dname_dbt, &iname_dbt, DB_SERIALIZABLE); // allocates memory for iname
+ char *iname = (char *) iname_dbt.data;
+ if (r == DB_NOTFOUND) {
+ r = ENOENT;
+ } else if (r == 0) {
+ // verify that newname does not already exist
+ r = db_getf_set(env->i->directory, txn, DB_SERIALIZABLE, &new_dname_dbt, ydb_getf_do_nothing, NULL);
+ if (r == 0) {
+ r = EEXIST;
+ }
+ else if (r == DB_NOTFOUND) {
+ // remove old (dname,iname) and insert (newname,iname) in directory
+ r = toku_db_del(env->i->directory, txn, &old_dname_dbt, DB_DELETE_ANY, true);
+ if (r != 0) { goto exit; }
+ r = toku_db_put(env->i->directory, txn, &new_dname_dbt, &iname_dbt, 0, true);
+ if (r != 0) { goto exit; }
+
+ //Now that we have writelocks on both dnames, verify that there are still no handles open. (to prevent race conditions)
+ if (env_is_db_with_dname_open(env, dname)) {
+ r = toku_ydb_do_error(env, EINVAL, "Cannot rename dictionary with an open handle.\n");
+ goto exit;
+ }
+ if (env_is_db_with_dname_open(env, newname)) {
+ r = toku_ydb_do_error(env, EINVAL, "Cannot rename dictionary; Dictionary with target name has an open handle.\n");
+ goto exit;
+ }
+
+ // we know a live db handle does not exist.
+ //
+ // use the internally opened db to try and get a table lock
+ //
+ // if we can't get it, then some txn needs the ft and we
+ // should return lock not granted.
+ //
+ // otherwise, we're okay in marking this ft as remove on
+ // commit. no new handles can open for this dictionary
+ // because the txn has directory write locks on the dname
+ if (txn && !can_acquire_table_lock(env, txn, iname)) {
+ r = DB_LOCK_NOTGRANTED;
+ }
+ // We don't do anything at the ft or cachetable layer for rename.
+ // We just update entries in the environment's directory.
+ }
+ }
+
+exit:
+ if (iname) {
+ toku_free(iname);
+ }
+ return r;
+}
+
+int
+DB_CREATE_FUN (DB ** db, DB_ENV * env, uint32_t flags) {
+ int r = toku_db_create(db, env, flags);
+ return r;
+}
+
+/* need db_strerror_r for multiple threads */
+
+const char *
+db_strerror(int error) {
+ char *errorstr;
+ if (error >= 0) {
+ errorstr = strerror(error);
+ if (errorstr)
+ return errorstr;
+ }
+
+ switch (error) {
+ case DB_BADFORMAT:
+ return "Database Bad Format (probably a corrupted database)";
+ case DB_NOTFOUND:
+ return "Not found";
+ case TOKUDB_OUT_OF_LOCKS:
+ return "Out of locks";
+ case TOKUDB_DICTIONARY_TOO_OLD:
+ return "Dictionary too old for this version of PerconaFT";
+ case TOKUDB_DICTIONARY_TOO_NEW:
+ return "Dictionary too new for this version of PerconaFT";
+ case TOKUDB_CANCELED:
+ return "User cancelled operation";
+ case TOKUDB_NO_DATA:
+ return "Ran out of data (not EOF)";
+ case TOKUDB_HUGE_PAGES_ENABLED:
+ return "Transparent huge pages are enabled but PerconaFT's memory allocator will oversubscribe main memory with transparent huge pages. This check can be disabled by setting the environment variable TOKU_HUGE_PAGES_OK.";
+ }
+
+ static char unknown_result[100]; // Race condition if two threads call this at the same time. However even in a bad case, it should be some sort of null-terminated string.
+ errorstr = unknown_result;
+ snprintf(errorstr, sizeof unknown_result, "Unknown error code: %d", error);
+ return errorstr;
+}
+
+const char *
+db_version(int *major, int *minor, int *patch) {
+ if (major)
+ *major = DB_VERSION_MAJOR;
+ if (minor)
+ *minor = DB_VERSION_MINOR;
+ if (patch)
+ *patch = DB_VERSION_PATCH;
+ return toku_product_name_strings.db_version;
+}
+
+// HACK: To ensure toku_pthread_yield gets included in the .so
+// non-static would require a prototype in a header
+// static (since unused) would give a warning
+// static + unused would not actually help toku_pthread_yield get in the .so
+// static + used avoids all the warnings and makes sure toku_pthread_yield is in the .so
+static void __attribute__((__used__))
+include_toku_pthread_yield (void) {
+ toku_pthread_yield();
+}
+
+// For test purposes only, translate dname to iname
+// YDB lock is NOT held when this function is called,
+// as it is called by user
+static int
+env_get_iname(DB_ENV* env, DBT* dname_dbt, DBT* iname_dbt) {
+ DB *directory = env->i->directory;
+ int r = autotxn_db_get(directory, NULL, dname_dbt, iname_dbt, DB_SERIALIZABLE|DB_PRELOCKED); // allocates memory for iname
+ return r;
+}
+
+// TODO 2216: Patch out this (dangerous) function when loader is working and
+// we don't need to test the low-level redirect anymore.
+// for use by test programs only, just a wrapper around ft call:
+int
+toku_test_db_redirect_dictionary(DB * db, const char * dname_of_new_file, DB_TXN *dbtxn) {
+ int r;
+ DBT dname_dbt;
+ DBT iname_dbt;
+ char * new_iname_in_env;
+
+ FT_HANDLE ft_handle = db->i->ft_handle;
+ TOKUTXN tokutxn = db_txn_struct_i(dbtxn)->tokutxn;
+
+ toku_fill_dbt(&dname_dbt, dname_of_new_file, strlen(dname_of_new_file)+1);
+ toku_init_dbt_flags(&iname_dbt, DB_DBT_REALLOC);
+ r = toku_db_get(db->dbenv->i->directory, dbtxn, &dname_dbt, &iname_dbt, DB_SERIALIZABLE); // allocates memory for iname
+ assert_zero(r);
+ new_iname_in_env = (char *) iname_dbt.data;
+
+ toku_multi_operation_client_lock(); //Must hold MO lock for dictionary_redirect.
+ r = toku_dictionary_redirect(new_iname_in_env, ft_handle, tokutxn);
+ toku_multi_operation_client_unlock();
+
+ toku_free(new_iname_in_env);
+ return r;
+}
+
+//Tets only function
+uint64_t
+toku_test_get_latest_lsn(DB_ENV *env) {
+ LSN rval = ZERO_LSN;
+ if (env && env->i->logger) {
+ rval = toku_logger_last_lsn(env->i->logger);
+ }
+ return rval.lsn;
+}
+
+int
+toku_test_get_checkpointing_user_data_status (void) {
+ return toku_cachetable_get_checkpointing_user_data_status();
+}
+
+#undef STATUS_VALUE
+#undef PERSISTENT_UPGRADE_STATUS_VALUE
+
+#include <toku_race_tools.h>
+void __attribute__((constructor)) toku_ydb_helgrind_ignore(void);
+void
+toku_ydb_helgrind_ignore(void) {
+ TOKU_VALGRIND_HG_DISABLE_CHECKING(&ydb_layer_status, sizeof ydb_layer_status);
+}
diff --git a/storage/tokudb/PerconaFT/src/ydb.h b/storage/tokudb/PerconaFT/src/ydb.h
new file mode 100644
index 00000000000..9d4e94c6f30
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb.h
@@ -0,0 +1,60 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+// Initialize the ydb library globals.
+// Called when the ydb library is loaded.
+int toku_ydb_init(void);
+
+// Called when the ydb library is unloaded.
+void toku_ydb_destroy(void);
+
+// db_env_create for the trace library
+int db_env_create_toku10(DB_ENV **, uint32_t) __attribute__((__visibility__("default")));
+
+// db_create for the trace library
+int db_create_toku10(DB **, DB_ENV *, uint32_t) __attribute__((__visibility__("default")));
+
+// test only function
+extern "C" int toku_test_db_redirect_dictionary(DB * db, const char * dname_of_new_file, DB_TXN *dbtxn) __attribute__((__visibility__("default")));
+
+extern "C" uint64_t toku_test_get_latest_lsn(DB_ENV *env) __attribute__((__visibility__("default")));
+
+// test-only function
+extern "C" int toku_test_get_checkpointing_user_data_status(void) __attribute__((__visibility__("default")));
diff --git a/storage/tokudb/PerconaFT/src/ydb_cursor.cc b/storage/tokudb/PerconaFT/src/ydb_cursor.cc
new file mode 100644
index 00000000000..015e302f1c6
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_cursor.cc
@@ -0,0 +1,890 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <db.h>
+#include "toku_assert.h"
+#include "ydb-internal.h"
+#include "ydb_cursor.h"
+#include "ydb_row_lock.h"
+#include "ft/cursor.h"
+
+static YDB_C_LAYER_STATUS_S ydb_c_layer_status;
+#ifdef STATUS_VALUE
+#undef STATUS_VALUE
+#endif
+#define STATUS_VALUE(x) ydb_c_layer_status.status[x].value.num
+
+#define STATUS_INIT(k,c,t,l,inc) TOKUFT_STATUS_INIT(ydb_c_layer_status, k, c, t, l, inc)
+
+static void
+ydb_c_layer_status_init (void) {
+ // Note, this function initializes the keyname, type, and legend fields.
+ // Value fields are initialized to zero by compiler.
+ ydb_c_layer_status.initialized = true;
+}
+#undef STATUS_INIT
+
+void
+ydb_c_layer_get_status(YDB_C_LAYER_STATUS statp) {
+ if (!ydb_c_layer_status.initialized)
+ ydb_c_layer_status_init();
+ *statp = ydb_c_layer_status;
+}
+
+//Get the main portion of a cursor flag (excluding the bitwise or'd components).
+static int
+get_main_cursor_flag(uint32_t flags) {
+ return flags & DB_OPFLAGS_MASK;
+}
+
+static int
+get_nonmain_cursor_flags(uint32_t flags) {
+ return flags & ~(DB_OPFLAGS_MASK);
+}
+
+static inline bool
+c_uninitialized(DBC *c) {
+ return toku_ft_cursor_uninitialized(dbc_ftcursor(c));
+}
+
+typedef struct query_context_wrapped_t {
+ DBT *key;
+ DBT *val;
+ struct simple_dbt *skey;
+ struct simple_dbt *sval;
+} *QUERY_CONTEXT_WRAPPED, QUERY_CONTEXT_WRAPPED_S;
+
+static inline void
+query_context_wrapped_init(QUERY_CONTEXT_WRAPPED context, DBC *c, DBT *key, DBT *val) {
+ context->key = key;
+ context->val = val;
+ context->skey = dbc_struct_i(c)->skey;
+ context->sval = dbc_struct_i(c)->sval;
+}
+
+static int
+c_get_wrapper_callback(DBT const *key, DBT const *val, void *extra) {
+ QUERY_CONTEXT_WRAPPED context = (QUERY_CONTEXT_WRAPPED) extra;
+ int r = toku_dbt_set(key->size, key->data, context->key, context->skey);
+ if (r == 0) {
+ r = toku_dbt_set(val->size, val->data, context->val, context->sval);
+ }
+ return r;
+}
+
+static inline uint32_t
+get_cursor_prelocked_flags(uint32_t flags, DBC* dbc) {
+ uint32_t lock_flags = flags & (DB_PRELOCKED | DB_PRELOCKED_WRITE);
+
+ //DB_READ_UNCOMMITTED and DB_READ_COMMITTED transactions 'own' all read locks for user-data dictionaries.
+ if (dbc_struct_i(dbc)->iso != TOKU_ISO_SERIALIZABLE) {
+ lock_flags |= DB_PRELOCKED;
+ }
+ return lock_flags;
+}
+
+//This is the user level callback function given to ydb layer functions like
+//c_getf_first
+
+typedef struct query_context_base_t {
+ FT_CURSOR c;
+ DB_TXN *txn;
+ DB *db;
+ YDB_CALLBACK_FUNCTION f;
+ void *f_extra;
+ int r_user_callback;
+ bool do_locking;
+ bool is_write_op;
+ toku::lock_request request;
+} *QUERY_CONTEXT_BASE, QUERY_CONTEXT_BASE_S;
+
+typedef struct query_context_t {
+ QUERY_CONTEXT_BASE_S base;
+} *QUERY_CONTEXT, QUERY_CONTEXT_S;
+
+typedef struct query_context_with_input_t {
+ QUERY_CONTEXT_BASE_S base;
+ DBT *input_key;
+ DBT *input_val;
+} *QUERY_CONTEXT_WITH_INPUT, QUERY_CONTEXT_WITH_INPUT_S;
+
+static void
+query_context_base_init(QUERY_CONTEXT_BASE context, DBC *c, uint32_t flag, bool is_write_op, YDB_CALLBACK_FUNCTION f, void *extra) {
+ context->c = dbc_ftcursor(c);
+ context->txn = dbc_struct_i(c)->txn;
+ context->db = c->dbp;
+ context->f = f;
+ context->f_extra = extra;
+ context->is_write_op = is_write_op;
+ uint32_t lock_flags = get_cursor_prelocked_flags(flag, c);
+ if (context->is_write_op) {
+ lock_flags &= DB_PRELOCKED_WRITE; // Only care about whether already locked for write
+ }
+ context->do_locking = (context->db->i->lt != nullptr && !(lock_flags & (DB_PRELOCKED | DB_PRELOCKED_WRITE)));
+ context->r_user_callback = 0;
+ context->request.create();
+}
+
+static toku::lock_request::type
+query_context_determine_lock_type(QUERY_CONTEXT_BASE context) {
+ return context->is_write_op ?
+ toku::lock_request::type::WRITE : toku::lock_request::type::READ;
+}
+
+static void
+query_context_base_destroy(QUERY_CONTEXT_BASE context) {
+ context->request.destroy();
+}
+
+static void
+query_context_init_read(QUERY_CONTEXT context, DBC *c, uint32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
+ const bool is_write = false;
+ query_context_base_init(&context->base, c, flag, is_write, f, extra);
+}
+
+static void
+query_context_init_write(QUERY_CONTEXT context, DBC *c, uint32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
+ const bool is_write = true;
+ query_context_base_init(&context->base, c, flag, is_write, f, extra);
+}
+
+static void
+query_context_with_input_init(QUERY_CONTEXT_WITH_INPUT context, DBC *c, uint32_t flag, DBT *key, DBT *val, YDB_CALLBACK_FUNCTION f, void *extra) {
+ // grab write locks if the DB_RMW flag is set or the cursor was created with the DB_RMW flag
+ const bool is_write = ((flag & DB_RMW) != 0) || dbc_struct_i(c)->rmw;
+ query_context_base_init(&context->base, c, flag, is_write, f, extra);
+ context->input_key = key;
+ context->input_val = val;
+}
+
+static int c_getf_first_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool);
+
+static void
+c_query_context_init(QUERY_CONTEXT context, DBC *c, uint32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
+ bool is_write_op = false;
+ // grab write locks if the DB_RMW flag is set or the cursor was created with the DB_RMW flag
+ if ((flag & DB_RMW) || dbc_struct_i(c)->rmw) {
+ is_write_op = true;
+ }
+ if (is_write_op) {
+ query_context_init_write(context, c, flag, f, extra);
+ } else {
+ query_context_init_read(context, c, flag, f, extra);
+ }
+}
+
+static void
+c_query_context_destroy(QUERY_CONTEXT context) {
+ query_context_base_destroy(&context->base);
+}
+
+static int
+c_getf_first(DBC *c, uint32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+ int r = 0;
+ QUERY_CONTEXT_S context; //Describes the context of this query.
+ c_query_context_init(&context, c, flag, f, extra);
+ while (r == 0) {
+ //toku_ft_cursor_first will call c_getf_first_callback(..., context) (if query is successful)
+ r = toku_ft_cursor_first(dbc_ftcursor(c), c_getf_first_callback, &context);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(context.base.db, context.base.txn, &context.base.request);
+ } else {
+ break;
+ }
+ }
+ c_query_context_destroy(&context);
+ return r;
+}
+
+//result is the result of the query (i.e. 0 means found, DB_NOTFOUND, etc..)
+static int
+c_getf_first_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool lock_only) {
+ QUERY_CONTEXT super_context = (QUERY_CONTEXT) extra;
+ QUERY_CONTEXT_BASE context = &super_context->base;
+
+ int r;
+ DBT found_key = { .data = (void *) key, .size = keylen };
+
+ if (context->do_locking) {
+ const DBT *left_key = toku_dbt_negative_infinity();
+ const DBT *right_key = key != NULL ? &found_key : toku_dbt_positive_infinity();
+ r = toku_db_start_range_lock(context->db, context->txn, left_key, right_key,
+ query_context_determine_lock_type(context), &context->request);
+ } else {
+ r = 0;
+ }
+
+ //Call application-layer callback if found and locks were successfully obtained.
+ if (r==0 && key!=NULL && !lock_only) {
+ DBT found_val = { .data = (void *) val, .size = vallen };
+ context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
+ r = context->r_user_callback;
+ }
+
+ //Give ft-layer an error (if any) to return from toku_ft_cursor_first
+ return r;
+}
+
+static int c_getf_last_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool);
+
+static int
+c_getf_last(DBC *c, uint32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+ int r = 0;
+ QUERY_CONTEXT_S context; //Describes the context of this query.
+ c_query_context_init(&context, c, flag, f, extra);
+ while (r == 0) {
+ //toku_ft_cursor_last will call c_getf_last_callback(..., context) (if query is successful)
+ r = toku_ft_cursor_last(dbc_ftcursor(c), c_getf_last_callback, &context);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(context.base.db, context.base.txn, &context.base.request);
+ } else {
+ break;
+ }
+ }
+ c_query_context_destroy(&context);
+ return r;
+}
+
+//result is the result of the query (i.e. 0 means found, DB_NOTFOUND, etc..)
+static int
+c_getf_last_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool lock_only) {
+ QUERY_CONTEXT super_context = (QUERY_CONTEXT) extra;
+ QUERY_CONTEXT_BASE context = &super_context->base;
+
+ int r;
+ DBT found_key = { .data = (void *) key, .size = keylen };
+
+ if (context->do_locking) {
+ const DBT *left_key = key != NULL ? &found_key : toku_dbt_negative_infinity();
+ const DBT *right_key = toku_dbt_positive_infinity();
+ r = toku_db_start_range_lock(context->db, context->txn, left_key, right_key,
+ query_context_determine_lock_type(context), &context->request);
+ } else {
+ r = 0;
+ }
+
+ //Call application-layer callback if found and locks were successfully obtained.
+ if (r==0 && key!=NULL && !lock_only) {
+ DBT found_val = { .data = (void *) val, .size = vallen };
+ context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
+ r = context->r_user_callback;
+ }
+
+ //Give ft-layer an error (if any) to return from toku_ft_cursor_last
+ return r;
+}
+
+static int c_getf_next_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool);
+
+static int
+c_getf_next(DBC *c, uint32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
+ int r;
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+ if (c_uninitialized(c)) {
+ r = c_getf_first(c, flag, f, extra);
+ } else {
+ r = 0;
+ QUERY_CONTEXT_S context; //Describes the context of this query.
+ c_query_context_init(&context, c, flag, f, extra);
+ while (r == 0) {
+ //toku_ft_cursor_next will call c_getf_next_callback(..., context) (if query is successful)
+ r = toku_ft_cursor_next(dbc_ftcursor(c), c_getf_next_callback, &context);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(context.base.db, context.base.txn, &context.base.request);
+ } else {
+ break;
+ }
+ }
+ c_query_context_destroy(&context);
+ }
+ return r;
+}
+
+//result is the result of the query (i.e. 0 means found, DB_NOTFOUND, etc..)
+static int
+c_getf_next_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool lock_only) {
+ QUERY_CONTEXT super_context = (QUERY_CONTEXT) extra;
+ QUERY_CONTEXT_BASE context = &super_context->base;
+
+ int r;
+
+ DBT found_key = { .data = (void *) key, .size = keylen };
+
+ if (context->do_locking) {
+ const DBT *prevkey, *prevval;
+ toku_ft_cursor_peek(context->c, &prevkey, &prevval);
+ const DBT *left_key = prevkey;
+ const DBT *right_key = key != NULL ? &found_key : toku_dbt_positive_infinity();
+ r = toku_db_start_range_lock(context->db, context->txn, left_key, right_key,
+ query_context_determine_lock_type(context), &context->request);
+ } else {
+ r = 0;
+ }
+
+ //Call application-layer callback if found and locks were successfully obtained.
+ if (r==0 && key!=NULL && !lock_only) {
+ DBT found_val = { .data = (void *) val, .size = vallen };
+ context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
+ r = context->r_user_callback;
+ }
+
+ //Give ft-layer an error (if any) to return from toku_ft_cursor_next
+ return r;
+}
+
+static int c_getf_prev_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool);
+
+static int
+c_getf_prev(DBC *c, uint32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
+ int r;
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+ if (c_uninitialized(c)) {
+ r = c_getf_last(c, flag, f, extra);
+ } else {
+ r = 0;
+ QUERY_CONTEXT_S context; //Describes the context of this query.
+ c_query_context_init(&context, c, flag, f, extra);
+ while (r == 0) {
+ //toku_ft_cursor_prev will call c_getf_prev_callback(..., context) (if query is successful)
+ r = toku_ft_cursor_prev(dbc_ftcursor(c), c_getf_prev_callback, &context);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(context.base.db, context.base.txn, &context.base.request);
+ } else {
+ break;
+ }
+ }
+ c_query_context_destroy(&context);
+ }
+ return r;
+}
+
+//result is the result of the query (i.e. 0 means found, DB_NOTFOUND, etc..)
+static int
+c_getf_prev_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool lock_only) {
+ QUERY_CONTEXT super_context = (QUERY_CONTEXT) extra;
+ QUERY_CONTEXT_BASE context = &super_context->base;
+
+ int r;
+ DBT found_key = { .data = (void *) key, .size = keylen };
+
+ if (context->do_locking) {
+ const DBT *prevkey, *prevval;
+ toku_ft_cursor_peek(context->c, &prevkey, &prevval);
+ const DBT *left_key = key != NULL ? &found_key : toku_dbt_negative_infinity();
+ const DBT *right_key = prevkey;
+ r = toku_db_start_range_lock(context->db, context->txn, left_key, right_key,
+ query_context_determine_lock_type(context), &context->request);
+ } else {
+ r = 0;
+ }
+
+ //Call application-layer callback if found and locks were successfully obtained.
+ if (r==0 && key!=NULL && !lock_only) {
+ DBT found_val = { .data = (void *) val, .size = vallen };
+ context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
+ r = context->r_user_callback;
+ }
+
+ //Give ft-layer an error (if any) to return from toku_ft_cursor_prev
+ return r;
+}
+
+static int c_getf_current_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool);
+
+static int
+c_getf_current(DBC *c, uint32_t flag, YDB_CALLBACK_FUNCTION f, void *extra) {
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+
+ QUERY_CONTEXT_S context; //Describes the context of this query.
+ c_query_context_init(&context, c, flag, f, extra);
+ //toku_ft_cursor_current will call c_getf_current_callback(..., context) (if query is successful)
+ int r = toku_ft_cursor_current(dbc_ftcursor(c), DB_CURRENT, c_getf_current_callback, &context);
+ c_query_context_destroy(&context);
+ return r;
+}
+
+//result is the result of the query (i.e. 0 means found, DB_NOTFOUND, etc..)
+static int
+c_getf_current_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool lock_only) {
+ QUERY_CONTEXT super_context = (QUERY_CONTEXT) extra;
+ QUERY_CONTEXT_BASE context = &super_context->base;
+
+ int r;
+
+ //Call application-layer callback if found.
+ if (key!=NULL && !lock_only) {
+ DBT found_key = { .data = (void *) key, .size = keylen };
+ DBT found_val = { .data = (void *) val, .size = vallen };
+ context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
+ r = context->r_user_callback;
+ } else {
+ r = 0;
+ }
+
+ //Give ft-layer an error (if any) to return from toku_ft_cursor_current
+ return r;
+}
+
+static int c_getf_set_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool);
+
+int
+toku_c_getf_set(DBC *c, uint32_t flag, DBT *key, YDB_CALLBACK_FUNCTION f, void *extra) {
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+
+ int r = 0;
+ QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
+ query_context_with_input_init(&context, c, flag, key, NULL, f, extra);
+ while (r == 0) {
+ //toku_ft_cursor_set will call c_getf_set_callback(..., context) (if query is successful)
+ r = toku_ft_cursor_set(dbc_ftcursor(c), key, c_getf_set_callback, &context);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(context.base.db, context.base.txn, &context.base.request);
+ } else {
+ break;
+ }
+ }
+ query_context_base_destroy(&context.base);
+ return r;
+}
+
+//result is the result of the query (i.e. 0 means found, DB_NOTFOUND, etc..)
+static int
+c_getf_set_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool lock_only) {
+ QUERY_CONTEXT_WITH_INPUT super_context = (QUERY_CONTEXT_WITH_INPUT) extra;
+ QUERY_CONTEXT_BASE context = &super_context->base;
+
+ int r;
+
+ //Lock:
+ // left(key,val) = (input_key, -infinity)
+ // right(key,val) = (input_key, found ? found_val : infinity)
+ if (context->do_locking) {
+ r = toku_db_start_range_lock(context->db, context->txn, super_context->input_key, super_context->input_key,
+ query_context_determine_lock_type(context), &context->request);
+ } else {
+ r = 0;
+ }
+
+ //Call application-layer callback if found and locks were successfully obtained.
+ if (r==0 && key!=NULL && !lock_only) {
+ DBT found_key = { .data = (void *) key, .size = keylen };
+ DBT found_val = { .data = (void *) val, .size = vallen };
+ context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
+ r = context->r_user_callback;
+ }
+
+ //Give ft-layer an error (if any) to return from toku_ft_cursor_set
+ return r;
+}
+
+static int c_getf_set_range_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool);
+
+static int
+c_getf_set_range(DBC *c, uint32_t flag, DBT *key, YDB_CALLBACK_FUNCTION f, void *extra) {
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+
+ int r = 0;
+ QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
+ query_context_with_input_init(&context, c, flag, key, NULL, f, extra);
+ while (r == 0) {
+ //toku_ft_cursor_set_range will call c_getf_set_range_callback(..., context) (if query is successful)
+ r = toku_ft_cursor_set_range(dbc_ftcursor(c), key, nullptr, c_getf_set_range_callback, &context);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(context.base.db, context.base.txn, &context.base.request);
+ } else {
+ break;
+ }
+ }
+ query_context_base_destroy(&context.base);
+ return r;
+}
+
+//result is the result of the query (i.e. 0 means found, DB_NOTFOUND, etc..)
+static int
+c_getf_set_range_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool lock_only) {
+ QUERY_CONTEXT_WITH_INPUT super_context = (QUERY_CONTEXT_WITH_INPUT) extra;
+ QUERY_CONTEXT_BASE context = &super_context->base;
+
+ int r;
+ DBT found_key = { .data = (void *) key, .size = keylen };
+
+ //Lock:
+ // left(key,val) = (input_key, -infinity)
+ // right(key) = found ? found_key : infinity
+ // right(val) = found ? found_val : infinity
+ if (context->do_locking) {
+ const DBT *left_key = super_context->input_key;
+ const DBT *right_key = key != NULL ? &found_key : toku_dbt_positive_infinity();
+ r = toku_db_start_range_lock(context->db, context->txn, left_key, right_key,
+ query_context_determine_lock_type(context), &context->request);
+ } else {
+ r = 0;
+ }
+
+ //Call application-layer callback if found and locks were successfully obtained.
+ if (r==0 && key!=NULL && !lock_only) {
+ DBT found_val = { .data = (void *) val, .size = vallen };
+ context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
+ r = context->r_user_callback;
+ }
+
+ //Give ft-layer an error (if any) to return from toku_ft_cursor_set_range
+ return r;
+}
+
+static int
+c_getf_set_range_with_bound(DBC *c, uint32_t flag, DBT *key, DBT *key_bound, YDB_CALLBACK_FUNCTION f, void *extra) {
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+
+ int r = 0;
+ QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
+ query_context_with_input_init(&context, c, flag, key, NULL, f, extra);
+ while (r == 0) {
+ //toku_ft_cursor_set_range will call c_getf_set_range_callback(..., context) (if query is successful)
+ r = toku_ft_cursor_set_range(dbc_ftcursor(c), key, key_bound, c_getf_set_range_callback, &context);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(context.base.db, context.base.txn, &context.base.request);
+ } else {
+ break;
+ }
+ }
+ query_context_base_destroy(&context.base);
+ return r;
+}
+
+static int c_getf_set_range_reverse_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool);
+
+static int
+c_getf_set_range_reverse(DBC *c, uint32_t flag, DBT *key, YDB_CALLBACK_FUNCTION f, void *extra) {
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+
+ int r = 0;
+ QUERY_CONTEXT_WITH_INPUT_S context; //Describes the context of this query.
+ query_context_with_input_init(&context, c, flag, key, NULL, f, extra);
+ while (r == 0) {
+ //toku_ft_cursor_set_range_reverse will call c_getf_set_range_reverse_callback(..., context) (if query is successful)
+ r = toku_ft_cursor_set_range_reverse(dbc_ftcursor(c), key, c_getf_set_range_reverse_callback, &context);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(context.base.db, context.base.txn, &context.base.request);
+ } else {
+ break;
+ }
+ }
+ query_context_base_destroy(&context.base);
+ return r;
+}
+
+//result is the result of the query (i.e. 0 means found, DB_NOTFOUND, etc..)
+static int
+c_getf_set_range_reverse_callback(uint32_t keylen, const void *key, uint32_t vallen, const void *val, void *extra, bool lock_only) {
+ QUERY_CONTEXT_WITH_INPUT super_context = (QUERY_CONTEXT_WITH_INPUT) extra;
+ QUERY_CONTEXT_BASE context = &super_context->base;
+
+ int r;
+ DBT found_key = { .data = (void *) key, .size = keylen };
+
+ //Lock:
+ // left(key) = found ? found_key : -infinity
+ // left(val) = found ? found_val : -infinity
+ // right(key,val) = (input_key, infinity)
+ if (context->do_locking) {
+ const DBT *left_key = key != NULL ? &found_key : toku_dbt_negative_infinity();
+ const DBT *right_key = super_context->input_key;
+ r = toku_db_start_range_lock(context->db, context->txn, left_key, right_key,
+ query_context_determine_lock_type(context), &context->request);
+ } else {
+ r = 0;
+ }
+
+ //Call application-layer callback if found and locks were successfully obtained.
+ if (r==0 && key!=NULL && !lock_only) {
+ DBT found_val = { .data = (void *) val, .size = vallen };
+ context->r_user_callback = context->f(&found_key, &found_val, context->f_extra);
+ r = context->r_user_callback;
+ }
+
+ //Give ft-layer an error (if any) to return from toku_ft_cursor_set_range_reverse
+ return r;
+}
+
+
+int toku_c_close_internal(DBC *c) {
+ toku_ft_cursor_destroy(dbc_ftcursor(c));
+ toku_sdbt_cleanup(&dbc_struct_i(c)->skey_s);
+ toku_sdbt_cleanup(&dbc_struct_i(c)->sval_s);
+ return 0;
+}
+
+// Close a cursor.
+int toku_c_close(DBC *c) {
+ toku_c_close_internal(c);
+ toku_free(c);
+ return 0;
+}
+
+static int
+c_set_bounds(DBC *dbc, const DBT *left_key, const DBT *right_key, bool pre_acquire, int out_of_range_error) {
+ if (out_of_range_error != DB_NOTFOUND &&
+ out_of_range_error != TOKUDB_OUT_OF_RANGE &&
+ out_of_range_error != 0) {
+ return toku_ydb_do_error(
+ dbc->dbp->dbenv,
+ EINVAL,
+ "Invalid out_of_range_error [%d] for %s\n",
+ out_of_range_error,
+ __FUNCTION__
+ );
+ }
+ if (left_key == toku_dbt_negative_infinity() && right_key == toku_dbt_positive_infinity()) {
+ out_of_range_error = 0;
+ }
+ DB *db = dbc->dbp;
+ DB_TXN *txn = dbc_struct_i(dbc)->txn;
+ HANDLE_PANICKED_DB(db);
+ toku_ft_cursor_set_range_lock(dbc_ftcursor(dbc), left_key, right_key,
+ (left_key == toku_dbt_negative_infinity()),
+ (right_key == toku_dbt_positive_infinity()),
+ out_of_range_error);
+ if (!db->i->lt || !txn || !pre_acquire)
+ return 0;
+ //READ_UNCOMMITTED and READ_COMMITTED transactions do not need read locks.
+ if (!dbc_struct_i(dbc)->rmw && dbc_struct_i(dbc)->iso != TOKU_ISO_SERIALIZABLE)
+ return 0;
+
+ toku::lock_request::type lock_type = dbc_struct_i(dbc)->rmw ?
+ toku::lock_request::type::WRITE : toku::lock_request::type::READ;
+ int r = toku_db_get_range_lock(db, txn, left_key, right_key, lock_type);
+ return r;
+}
+
+static void
+c_remove_restriction(DBC *dbc) {
+ toku_ft_cursor_remove_restriction(dbc_ftcursor(dbc));
+}
+
+static void c_set_txn(DBC *dbc, DB_TXN *txn) {
+ dbc_struct_i(dbc)->txn = txn;
+ dbc_ftcursor(dbc)->ttxn = db_txn_struct_i(txn)->tokutxn;
+}
+
+static void
+c_set_check_interrupt_callback(DBC* dbc, bool (*interrupt_callback)(void*, uint64_t), void *extra) {
+ toku_ft_cursor_set_check_interrupt_cb(dbc_ftcursor(dbc), interrupt_callback, extra);
+}
+
+int
+toku_c_get(DBC* c, DBT* key, DBT* val, uint32_t flag) {
+ HANDLE_PANICKED_DB(c->dbp);
+ HANDLE_CURSOR_ILLEGAL_WORKING_PARENT_TXN(c);
+
+ uint32_t main_flag = get_main_cursor_flag(flag);
+ uint32_t remaining_flags = get_nonmain_cursor_flags(flag);
+ int r;
+ QUERY_CONTEXT_WRAPPED_S context;
+ //Passing in NULL for a key or val means that it is NOT an output.
+ // Both key and val are output:
+ // query_context_wrapped_init(&context, c, key, val);
+ // Val is output, key is not:
+ // query_context_wrapped_init(&context, c, NULL, val);
+ // Neither key nor val are output:
+ // query_context_wrapped_init(&context, c, NULL, NULL);
+ switch (main_flag) {
+ case (DB_FIRST):
+ query_context_wrapped_init(&context, c, key, val);
+ r = c_getf_first(c, remaining_flags, c_get_wrapper_callback, &context);
+ break;
+ case (DB_LAST):
+ query_context_wrapped_init(&context, c, key, val);
+ r = c_getf_last(c, remaining_flags, c_get_wrapper_callback, &context);
+ break;
+ case (DB_NEXT):
+ query_context_wrapped_init(&context, c, key, val);
+ r = c_getf_next(c, remaining_flags, c_get_wrapper_callback, &context);
+ break;
+ case (DB_PREV):
+ query_context_wrapped_init(&context, c, key, val);
+ r = c_getf_prev(c, remaining_flags, c_get_wrapper_callback, &context);
+ break;
+#ifdef DB_PREV_DUP
+ case (DB_PREV_DUP):
+ query_context_wrapped_init(&context, c, key, val);
+ r = toku_c_getf_prev_dup(c, remaining_flags, c_get_wrapper_callback, &context);
+ break;
+#endif
+ case (DB_CURRENT):
+ query_context_wrapped_init(&context, c, key, val);
+ r = c_getf_current(c, remaining_flags, c_get_wrapper_callback, &context);
+ break;
+ case (DB_SET):
+ query_context_wrapped_init(&context, c, NULL, val);
+ r = toku_c_getf_set(c, remaining_flags, key, c_get_wrapper_callback, &context);
+ break;
+ case (DB_SET_RANGE):
+ query_context_wrapped_init(&context, c, key, val);
+ r = c_getf_set_range(c, remaining_flags, key, c_get_wrapper_callback, &context);
+ break;
+ case (DB_SET_RANGE_REVERSE):
+ query_context_wrapped_init(&context, c, key, val);
+ r = c_getf_set_range_reverse(c, remaining_flags, key, c_get_wrapper_callback, &context);
+ break;
+ default:
+ r = EINVAL;
+ break;
+ }
+ return r;
+}
+
+int
+toku_db_cursor_internal(DB * db, DB_TXN * txn, DBC *c, uint32_t flags, int is_temporary_cursor) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ DB_ENV* env = db->dbenv;
+
+ if (flags & ~(DB_SERIALIZABLE | DB_INHERIT_ISOLATION | DB_RMW | DBC_DISABLE_PREFETCHING)) {
+ return toku_ydb_do_error(
+ env,
+ EINVAL,
+ "Invalid flags set for toku_db_cursor\n"
+ );
+ }
+
+#define SCRS(name) c->name = name
+ SCRS(c_getf_first);
+ SCRS(c_getf_last);
+ SCRS(c_getf_next);
+ SCRS(c_getf_prev);
+ SCRS(c_getf_current);
+ SCRS(c_getf_set_range);
+ SCRS(c_getf_set_range_reverse);
+ SCRS(c_getf_set_range_with_bound);
+ SCRS(c_set_bounds);
+ SCRS(c_remove_restriction);
+ SCRS(c_set_txn);
+ SCRS(c_set_check_interrupt_callback);
+#undef SCRS
+
+ c->c_get = toku_c_get;
+ c->c_getf_set = toku_c_getf_set;
+ c->c_close = toku_c_close;
+
+ c->dbp = db;
+
+ dbc_struct_i(c)->txn = txn;
+ dbc_struct_i(c)->skey_s = (struct simple_dbt){0,0};
+ dbc_struct_i(c)->sval_s = (struct simple_dbt){0,0};
+ if (is_temporary_cursor) {
+ dbc_struct_i(c)->skey = &db->i->skey;
+ dbc_struct_i(c)->sval = &db->i->sval;
+ } else {
+ dbc_struct_i(c)->skey = &dbc_struct_i(c)->skey_s;
+ dbc_struct_i(c)->sval = &dbc_struct_i(c)->sval_s;
+ }
+ if (flags & DB_SERIALIZABLE) {
+ dbc_struct_i(c)->iso = TOKU_ISO_SERIALIZABLE;
+ } else {
+ dbc_struct_i(c)->iso = txn ? db_txn_struct_i(txn)->iso : TOKU_ISO_SERIALIZABLE;
+ }
+ dbc_struct_i(c)->rmw = (flags & DB_RMW) != 0;
+ enum cursor_read_type read_type = C_READ_ANY; // default, used in serializable and read uncommitted
+ if (txn) {
+ if (dbc_struct_i(c)->iso == TOKU_ISO_READ_COMMITTED ||
+ dbc_struct_i(c)->iso == TOKU_ISO_SNAPSHOT)
+ {
+ read_type = C_READ_SNAPSHOT;
+ }
+ else if (dbc_struct_i(c)->iso == TOKU_ISO_READ_COMMITTED_ALWAYS) {
+ read_type = C_READ_COMMITTED;
+ }
+ }
+ int r = toku_ft_cursor_create(
+ db->i->ft_handle,
+ dbc_ftcursor(c),
+ txn ? db_txn_struct_i(txn)->tokutxn : NULL,
+ read_type,
+ ((flags & DBC_DISABLE_PREFETCHING) != 0),
+ is_temporary_cursor != 0
+ );
+ if (r != 0) {
+ invariant(r == TOKUDB_MVCC_DICTIONARY_TOO_NEW);
+ }
+ return r;
+}
+
+static inline int
+autotxn_db_cursor(DB *db, DB_TXN *txn, DBC *c, uint32_t flags) {
+ if (!txn && (db->dbenv->i->open_flags & DB_INIT_TXN)) {
+ return toku_ydb_do_error(db->dbenv, EINVAL,
+ "Cursors in a transaction environment must have transactions.\n");
+ }
+ return toku_db_cursor_internal(db, txn, c, flags, 0);
+}
+
+// Create a cursor on a db.
+int toku_db_cursor(DB *db, DB_TXN *txn, DBC **c, uint32_t flags) {
+ DBC *XMALLOC(cursor);
+ int r = autotxn_db_cursor(db, txn, cursor, flags);
+ if (r == 0) {
+ *c = cursor;
+ } else {
+ toku_free(cursor);
+ }
+ return r;
+}
+
+#undef STATUS_VALUE
+
+#include <toku_race_tools.h>
+void __attribute__((constructor)) toku_ydb_cursor_helgrind_ignore(void);
+void
+toku_ydb_cursor_helgrind_ignore(void) {
+ TOKU_VALGRIND_HG_DISABLE_CHECKING(&ydb_c_layer_status, sizeof ydb_c_layer_status);
+}
diff --git a/storage/tokudb/PerconaFT/src/ydb_cursor.h b/storage/tokudb/PerconaFT/src/ydb_cursor.h
new file mode 100644
index 00000000000..232f2670df8
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_cursor.h
@@ -0,0 +1,61 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+// This file defines the public interface to the ydb library
+
+typedef enum {
+ YDB_C_LAYER_STATUS_NUM_ROWS = 0 /* number of rows in this status array */
+} ydb_c_lock_layer_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[YDB_C_LAYER_STATUS_NUM_ROWS];
+} YDB_C_LAYER_STATUS_S, *YDB_C_LAYER_STATUS;
+
+void ydb_c_layer_get_status(YDB_C_LAYER_STATUS statp);
+
+int toku_c_get(DBC * c, DBT * key, DBT * data, uint32_t flag);
+int toku_c_getf_set(DBC *c, uint32_t flag, DBT *key, YDB_CALLBACK_FUNCTION f, void *extra);
+
+int toku_db_cursor(DB *db, DB_TXN *txn, DBC **c, uint32_t flags);
+int toku_db_cursor_internal(DB *db, DB_TXN * txn, DBC *c, uint32_t flags, int is_temporary_cursor);
+
+int toku_c_close(DBC *c);
+int toku_c_close_internal(DBC *c);
diff --git a/storage/tokudb/PerconaFT/src/ydb_db.cc b/storage/tokudb/PerconaFT/src/ydb_db.cc
new file mode 100644
index 00000000000..25b24467684
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_db.cc
@@ -0,0 +1,1211 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <ctype.h>
+
+#include <db.h>
+#include <locktree/locktree.h>
+#include <ft/ft.h>
+#include <ft/ft-flusher.h>
+#include <ft/cachetable/checkpoint.h>
+
+#include "ydb_cursor.h"
+#include "ydb_row_lock.h"
+#include "ydb_db.h"
+#include "ydb_write.h"
+#include "ydb-internal.h"
+#include "ydb_load.h"
+#include "indexer.h"
+#include <portability/toku_atomic.h>
+#include <util/status.h>
+#include <ft/le-cursor.h>
+
+static YDB_DB_LAYER_STATUS_S ydb_db_layer_status;
+#ifdef STATUS_VALUE
+#undef STATUS_VALUE
+#endif
+#define STATUS_VALUE(x) ydb_db_layer_status.status[x].value.num
+
+#define STATUS_INIT(k,c,t,l,inc) TOKUFT_STATUS_INIT(ydb_db_layer_status, k, c, t, l, inc)
+
+static void
+ydb_db_layer_status_init (void) {
+ // Note, this function initializes the keyname, type, and legend fields.
+ // Value fields are initialized to zero by compiler.
+
+ STATUS_INIT(YDB_LAYER_DIRECTORY_WRITE_LOCKS, nullptr, UINT64, "directory write locks", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_DIRECTORY_WRITE_LOCKS_FAIL, nullptr, UINT64, "directory write locks fail", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_LOGSUPPRESS, nullptr, UINT64, "log suppress", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_LOGSUPPRESS_FAIL, nullptr, UINT64, "log suppress fail", TOKU_ENGINE_STATUS);
+ ydb_db_layer_status.initialized = true;
+}
+#undef STATUS_INIT
+
+void
+ydb_db_layer_get_status(YDB_DB_LAYER_STATUS statp) {
+ if (!ydb_db_layer_status.initialized)
+ ydb_db_layer_status_init();
+ *statp = ydb_db_layer_status;
+}
+
+static void
+create_iname_hint(const char *dname, char *hint) {
+ //Requires: size of hint array must be > strlen(dname)
+ //Copy alphanumeric characters only.
+ //Replace strings of non-alphanumeric characters with a single underscore.
+ bool underscored = false;
+ while (*dname) {
+ if (isalnum(*dname)) {
+ char c = *dname++;
+ *hint++ = c;
+ underscored = false;
+ }
+ else {
+ if (!underscored)
+ *hint++ = '_';
+ dname++;
+ underscored = true;
+ }
+ }
+ *hint = '\0';
+}
+
+// n < 0 means to ignore mark and ignore n
+// n >= 0 means to include mark ("_B_" or "_P_") with hex value of n in iname
+// (intended for use by loader, which will create many inames using one txnid).
+static char *
+create_iname(DB_ENV *env, uint64_t id1, uint64_t id2, char *hint, const char *mark, int n) {
+ int bytes;
+ char inamebase[strlen(hint) +
+ 8 + // hex file format version
+ 24 + // hex id (normally the txnid's parent and child)
+ 8 + // hex value of n if non-neg
+ sizeof("_B___.") + // extra pieces
+ strlen(toku_product_name)];
+ if (n < 0)
+ bytes = snprintf(inamebase, sizeof(inamebase),
+ "%s_%" PRIx64 "_%" PRIx64 "_%" PRIx32 ".%s",
+ hint, id1, id2, FT_LAYOUT_VERSION, toku_product_name);
+ else {
+ invariant(strlen(mark) == 1);
+ bytes = snprintf(inamebase, sizeof(inamebase),
+ "%s_%" PRIx64 "_%" PRIx64 "_%" PRIx32 "_%s_%" PRIx32 ".%s",
+ hint, id1, id2, FT_LAYOUT_VERSION, mark, n, toku_product_name);
+ }
+ assert(bytes>0);
+ assert(bytes<=(int)sizeof(inamebase)-1);
+ char *rval;
+ if (env->i->data_dir)
+ rval = toku_construct_full_name(2, env->i->data_dir, inamebase);
+ else
+ rval = toku_construct_full_name(1, inamebase);
+ assert(rval);
+ return rval;
+}
+
+static int toku_db_open(DB * db, DB_TXN * txn, const char *fname, const char *dbname, DBTYPE dbtype, uint32_t flags, int mode);
+
+// Effect: Do the work required of DB->close().
+// requires: the multi_operation client lock is held.
+int
+toku_db_close(DB * db) {
+ int r = 0;
+ if (db_opened(db) && db->i->dname) {
+ // internal (non-user) dictionary has no dname
+ env_note_db_closed(db->dbenv, db); // tell env that this db is no longer in use by the user of this api (user-closed, may still be in use by fractal tree internals)
+ }
+ // close the ft handle, and possibly close the locktree
+ toku_ft_handle_close(db->i->ft_handle);
+ if (db->i->lt) {
+ db->dbenv->i->ltm.release_lt(db->i->lt);
+ }
+ toku_sdbt_cleanup(&db->i->skey);
+ toku_sdbt_cleanup(&db->i->sval);
+ if (db->i->dname) {
+ toku_free(db->i->dname);
+ }
+ toku_free(db->i);
+ toku_free(db);
+ return r;
+}
+
+///////////
+//db_getf_XXX is equivalent to c_getf_XXX, without a persistent cursor
+
+int
+db_getf_set(DB *db, DB_TXN *txn, uint32_t flags, DBT *key, YDB_CALLBACK_FUNCTION f, void *extra) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ DBC c;
+ uint32_t create_flags = flags & (DB_ISOLATION_FLAGS | DB_RMW);
+ flags &= ~DB_ISOLATION_FLAGS;
+ int r = toku_db_cursor_internal(db, txn, &c, create_flags | DBC_DISABLE_PREFETCHING, 1);
+ if (r==0) {
+ r = toku_c_getf_set(&c, flags, key, f, extra);
+ int r2 = toku_c_close_internal(&c);
+ if (r==0) r = r2;
+ }
+ return r;
+}
+
+static inline int
+db_thread_need_flags(DBT *dbt) {
+ return (dbt->flags & (DB_DBT_MALLOC+DB_DBT_REALLOC+DB_DBT_USERMEM)) == 0;
+}
+
+int
+toku_db_get (DB * db, DB_TXN * txn, DBT * key, DBT * data, uint32_t flags) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ int r;
+ uint32_t iso_flags = flags & DB_ISOLATION_FLAGS;
+
+ if ((db->i->open_flags & DB_THREAD) && db_thread_need_flags(data))
+ return EINVAL;
+
+ uint32_t lock_flags = flags & (DB_PRELOCKED | DB_PRELOCKED_WRITE);
+ flags &= ~lock_flags;
+ flags &= ~DB_ISOLATION_FLAGS;
+ // And DB_GET_BOTH is no longer supported. #2862.
+ if (flags != 0) return EINVAL;
+
+ DBC dbc;
+ r = toku_db_cursor_internal(db, txn, &dbc, iso_flags | DBC_DISABLE_PREFETCHING, 1);
+ if (r!=0) return r;
+ uint32_t c_get_flags = DB_SET;
+ r = toku_c_get(&dbc, key, data, c_get_flags | lock_flags);
+ int r2 = toku_c_close_internal(&dbc);
+ return r ? r : r2;
+}
+
+static int
+db_open_subdb(DB * db, DB_TXN * txn, const char *fname, const char *dbname, DBTYPE dbtype, uint32_t flags, int mode) {
+ int r;
+ if (!fname || !dbname) r = EINVAL;
+ else {
+ char subdb_full_name[strlen(fname) + sizeof("/") + strlen(dbname)];
+ int bytes = snprintf(subdb_full_name, sizeof(subdb_full_name), "%s/%s", fname, dbname);
+ assert(bytes==(int)sizeof(subdb_full_name)-1);
+ const char *null_subdbname = NULL;
+ r = toku_db_open(db, txn, subdb_full_name, null_subdbname, dbtype, flags, mode);
+ }
+ return r;
+}
+
+static uint64_t nontransactional_open_id = 0;
+
+// inames are created here.
+// algorithm:
+// begin txn
+// convert dname to iname (possibly creating new iname)
+// open file (toku_ft_handle_open() will handle logging)
+// close txn
+// if created a new iname, take full range lock
+// Requires: no checkpoint may take place during this function, which is enforced by holding the multi_operation_client_lock.
+static int
+toku_db_open(DB * db, DB_TXN * txn, const char *fname, const char *dbname, DBTYPE dbtype, uint32_t flags, int mode) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_READ_ONLY_TXN(txn);
+ if (dbname != NULL) {
+ return db_open_subdb(db, txn, fname, dbname, dbtype, flags, mode);
+ }
+
+ // at this point fname is the dname
+ //This code ONLY supports single-db files.
+ assert(dbname == NULL);
+ const char * dname = fname; // db_open_subdb() converts (fname, dbname) to dname
+
+ ////////////////////////////// do some level of parameter checking.
+ uint32_t unused_flags = flags;
+ int r;
+ if (dbtype!=DB_BTREE && dbtype!=DB_UNKNOWN) return EINVAL;
+ int is_db_excl = flags & DB_EXCL; unused_flags&=~DB_EXCL;
+ int is_db_create = flags & DB_CREATE; unused_flags&=~DB_CREATE;
+ int is_db_hot_index = flags & DB_IS_HOT_INDEX; unused_flags&=~DB_IS_HOT_INDEX;
+
+ //We support READ_UNCOMMITTED and READ_COMMITTED whether or not the flag is provided.
+ unused_flags&=~DB_READ_UNCOMMITTED;
+ unused_flags&=~DB_READ_COMMITTED;
+ unused_flags&=~DB_SERIALIZABLE;
+
+ // DB_THREAD is implicitly supported and DB_BLACKHOLE is supported at the ft-layer
+ unused_flags &= ~DB_THREAD;
+ unused_flags &= ~DB_BLACKHOLE;
+
+ // check for unknown or conflicting flags
+ if (unused_flags) return EINVAL; // unknown flags
+ if (is_db_excl && !is_db_create) return EINVAL;
+ if (dbtype==DB_UNKNOWN && is_db_excl) return EINVAL;
+
+ if (db_opened(db)) {
+ // it was already open
+ return EINVAL;
+ }
+ //////////////////////////////
+
+ // convert dname to iname
+ // - look up dname, get iname
+ // - if dname does not exist, create iname and make entry in directory
+ DBT dname_dbt; // holds dname
+ DBT iname_dbt; // holds iname_in_env
+ toku_fill_dbt(&dname_dbt, dname, strlen(dname)+1);
+ toku_init_dbt_flags(&iname_dbt, DB_DBT_REALLOC);
+ r = toku_db_get(db->dbenv->i->directory, txn, &dname_dbt, &iname_dbt, DB_SERIALIZABLE); // allocates memory for iname
+ char *iname = (char *) iname_dbt.data;
+ if (r == DB_NOTFOUND && !is_db_create) {
+ r = ENOENT;
+ } else if (r==0 && is_db_excl) {
+ r = EEXIST;
+ } else if (r == DB_NOTFOUND) {
+ char hint[strlen(dname) + 1];
+
+ // create iname and make entry in directory
+ uint64_t id1 = 0;
+ uint64_t id2 = 0;
+
+ if (txn) {
+ id1 = toku_txn_get_txnid(db_txn_struct_i(txn)->tokutxn).parent_id64;
+ id2 = toku_txn_get_txnid(db_txn_struct_i(txn)->tokutxn).child_id64;
+ } else {
+ id1 = toku_sync_fetch_and_add(&nontransactional_open_id, 1);
+ }
+ create_iname_hint(dname, hint);
+ iname = create_iname(db->dbenv, id1, id2, hint, NULL, -1); // allocated memory for iname
+ toku_fill_dbt(&iname_dbt, iname, strlen(iname) + 1);
+ //
+ // put_flags will be 0 for performance only, avoid unnecessary query
+ // if we are creating a hot index, per #3166, we do not want the write lock in directory grabbed.
+ // directory read lock is grabbed in toku_db_get above
+ //
+ uint32_t put_flags = 0 | ((is_db_hot_index) ? DB_PRELOCKED_WRITE : 0);
+ r = toku_db_put(db->dbenv->i->directory, txn, &dname_dbt, &iname_dbt, put_flags, true);
+ }
+
+ // we now have an iname
+ if (r == 0) {
+ r = toku_db_open_iname(db, txn, iname, flags, mode);
+ if (r == 0) {
+ db->i->dname = toku_xstrdup(dname);
+ env_note_db_opened(db->dbenv, db); // tell env that a new db handle is open (using dname)
+ }
+ }
+
+ if (iname) {
+ toku_free(iname);
+ }
+ return r;
+}
+
+// set the descriptor and cmp_descriptor to the
+// descriptors from the given ft, updating the
+// locktree's descriptor pointer if necessary
+static void
+db_set_descriptors(DB *db, FT_HANDLE ft_handle) {
+ const toku::comparator &cmp = toku_ft_get_comparator(ft_handle);
+ db->descriptor = toku_ft_get_descriptor(ft_handle);
+ db->cmp_descriptor = toku_ft_get_cmp_descriptor(ft_handle);
+ invariant(db->cmp_descriptor == cmp.get_descriptor());
+ if (db->i->lt) {
+ db->i->lt->set_comparator(cmp);
+ }
+}
+
+// callback that sets the descriptors when
+// a dictionary is redirected at the ft layer
+static void
+db_on_redirect_callback(FT_HANDLE ft_handle, void* extra) {
+ DB *db = (DB *) extra;
+ db_set_descriptors(db, ft_handle);
+}
+
+// when a locktree is created, clone a ft handle and store it
+// as userdata so we can close it later.
+int toku_db_lt_on_create_callback(toku::locktree *lt, void *extra) {
+ int r;
+ struct lt_on_create_callback_extra *info = (struct lt_on_create_callback_extra *) extra;
+ TOKUTXN ttxn = info->txn ? db_txn_struct_i(info->txn)->tokutxn : NULL;
+ FT_HANDLE ft_handle = info->ft_handle;
+
+ FT_HANDLE cloned_ft_handle;
+ r = toku_ft_handle_clone(&cloned_ft_handle, ft_handle, ttxn);
+ if (r == 0) {
+ assert(lt->get_userdata() == NULL);
+ lt->set_userdata(cloned_ft_handle);
+ }
+ return r;
+}
+
+// when a locktree is about to be destroyed,
+// close the ft handle stored as userdata.
+void toku_db_lt_on_destroy_callback(toku::locktree *lt) {
+ FT_HANDLE ft_handle = (FT_HANDLE) lt->get_userdata();
+ assert(ft_handle);
+ toku_ft_handle_close(ft_handle);
+}
+
+// Instruct db to use the default (built-in) key comparison function
+// by setting the flag bits in the db and ft structs
+int toku_db_use_builtin_key_cmp(DB *db) {
+ HANDLE_PANICKED_DB(db);
+ int r = 0;
+ if (db_opened(db)) {
+ r = toku_ydb_do_error(db->dbenv, EINVAL, "Comparison functions cannot be set after DB open.\n");
+ } else if (db->i->key_compare_was_set) {
+ r = toku_ydb_do_error(db->dbenv, EINVAL, "Key comparison function already set.\n");
+ } else {
+ uint32_t tflags;
+ toku_ft_get_flags(db->i->ft_handle, &tflags);
+
+ tflags |= TOKU_DB_KEYCMP_BUILTIN;
+ toku_ft_set_flags(db->i->ft_handle, tflags);
+ db->i->key_compare_was_set = true;
+ }
+ return r;
+}
+
+int toku_db_open_iname(DB * db, DB_TXN * txn, const char *iname_in_env, uint32_t flags, int mode) {
+ //Set comparison functions if not yet set.
+ HANDLE_READ_ONLY_TXN(txn);
+ if (!db->i->key_compare_was_set && db->dbenv->i->bt_compare) {
+ toku_ft_set_bt_compare(db->i->ft_handle, db->dbenv->i->bt_compare);
+ db->i->key_compare_was_set = true;
+ }
+ if (db->dbenv->i->update_function) {
+ toku_ft_set_update(db->i->ft_handle,db->dbenv->i->update_function);
+ }
+ toku_ft_set_redirect_callback(
+ db->i->ft_handle,
+ db_on_redirect_callback,
+ db
+ );
+ bool need_locktree = (bool)((db->dbenv->i->open_flags & DB_INIT_LOCK) &&
+ (db->dbenv->i->open_flags & DB_INIT_TXN));
+
+ int is_db_excl = flags & DB_EXCL; flags&=~DB_EXCL;
+ int is_db_create = flags & DB_CREATE; flags&=~DB_CREATE;
+ //We support READ_UNCOMMITTED and READ_COMMITTED whether or not the flag is provided.
+ flags&=~DB_READ_UNCOMMITTED;
+ flags&=~DB_READ_COMMITTED;
+ flags&=~DB_SERIALIZABLE;
+ flags&=~DB_IS_HOT_INDEX;
+ // unknown or conflicting flags are bad
+ int unknown_flags = flags & ~DB_THREAD;
+ unknown_flags &= ~DB_BLACKHOLE;
+ if (unknown_flags || (is_db_excl && !is_db_create)) {
+ return EINVAL;
+ }
+
+ if (db_opened(db)) {
+ return EINVAL; /* It was already open. */
+ }
+
+ db->i->open_flags = flags;
+ db->i->open_mode = mode;
+
+ FT_HANDLE ft_handle = db->i->ft_handle;
+ int r = toku_ft_handle_open(ft_handle, iname_in_env,
+ is_db_create, is_db_excl,
+ db->dbenv->i->cachetable,
+ txn ? db_txn_struct_i(txn)->tokutxn : nullptr);
+ if (r != 0) {
+ goto out;
+ }
+
+ // if the dictionary was opened as a blackhole, mark the
+ // fractal tree as blackhole too.
+ if (flags & DB_BLACKHOLE) {
+ toku_ft_set_blackhole(ft_handle);
+ }
+
+ db->i->opened = 1;
+
+ // now that the handle has successfully opened, a valid descriptor
+ // is in the ft. we need to set the db's descriptor pointers
+ db_set_descriptors(db, ft_handle);
+
+ if (need_locktree) {
+ db->i->dict_id = toku_ft_get_dictionary_id(db->i->ft_handle);
+ struct lt_on_create_callback_extra on_create_extra = {
+ .txn = txn,
+ .ft_handle = db->i->ft_handle,
+ };
+ db->i->lt = db->dbenv->i->ltm.get_lt(db->i->dict_id,
+ toku_ft_get_comparator(db->i->ft_handle),
+ &on_create_extra);
+ if (db->i->lt == nullptr) {
+ r = errno;
+ if (r == 0) {
+ r = EINVAL;
+ }
+ goto out;
+ }
+ }
+ r = 0;
+
+out:
+ if (r != 0) {
+ db->i->dict_id = DICTIONARY_ID_NONE;
+ db->i->opened = 0;
+ if (db->i->lt) {
+ db->dbenv->i->ltm.release_lt(db->i->lt);
+ db->i->lt = nullptr;
+ }
+ }
+ return r;
+}
+
+// Return the maximum key and val size in
+// *key_size and *val_size respectively
+static void
+toku_db_get_max_row_size(DB * UU(db), uint32_t * max_key_size, uint32_t * max_val_size) {
+ *max_key_size = 0;
+ *max_val_size = 0;
+ toku_ft_get_maximum_advised_key_value_lengths(max_key_size, max_val_size);
+}
+
+int toku_db_pre_acquire_fileops_lock(DB *db, DB_TXN *txn) {
+ // bad hack because some environment dictionaries do not have a dname
+ char *dname = db->i->dname;
+ if (!dname)
+ return 0;
+
+ DBT key_in_directory = { .data = dname, .size = (uint32_t) strlen(dname)+1 };
+ //Left end of range == right end of range (point lock)
+ int r = toku_db_get_range_lock(db->dbenv->i->directory, txn,
+ &key_in_directory, &key_in_directory,
+ toku::lock_request::type::WRITE);
+ if (r == 0)
+ STATUS_VALUE(YDB_LAYER_DIRECTORY_WRITE_LOCKS)++; // accountability
+ else
+ STATUS_VALUE(YDB_LAYER_DIRECTORY_WRITE_LOCKS_FAIL)++; // accountability
+ return r;
+}
+
+//
+// This function is used both to set an initial descriptor of a DB and to
+// change a descriptor. (only way to set a descriptor of a DB)
+//
+// Requires:
+// - The caller must not call put_multiple, del_multiple, or update_multiple concurrently
+// - The caller must not have a hot index running concurrently on db
+// - If the caller has passed DB_UPDATE_CMP_DESCRIPTOR as a flag, then he is calling this function
+// ONLY immediately after creating the dictionary and before doing any actual work on the dictionary.
+//
+static int
+toku_db_change_descriptor(DB *db, DB_TXN* txn, const DBT* descriptor, uint32_t flags) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_READ_ONLY_TXN(txn);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ int r = 0;
+ TOKUTXN ttxn = txn ? db_txn_struct_i(txn)->tokutxn : NULL;
+ bool is_db_hot_index = ((flags & DB_IS_HOT_INDEX) != 0);
+ bool update_cmp_descriptor = ((flags & DB_UPDATE_CMP_DESCRIPTOR) != 0);
+
+ DBT old_descriptor_dbt;
+ toku_init_dbt(&old_descriptor_dbt);
+
+ if (!db_opened(db) || !descriptor || (descriptor->size>0 && !descriptor->data)){
+ r = EINVAL;
+ goto cleanup;
+ }
+ // For a hot index, this is an initial descriptor.
+ // We do not support (yet) hcad with hot index concurrently on a single table, which
+ // would require changing a descriptor for a hot index.
+ if (!is_db_hot_index) {
+ r = toku_db_pre_acquire_table_lock(db, txn);
+ if (r != 0) { goto cleanup; }
+ }
+
+ toku_clone_dbt(&old_descriptor_dbt, db->descriptor->dbt);
+ toku_ft_change_descriptor(db->i->ft_handle, &old_descriptor_dbt, descriptor,
+ true, ttxn, update_cmp_descriptor);
+
+cleanup:
+ toku_destroy_dbt(&old_descriptor_dbt);
+ return r;
+}
+
+static int
+toku_db_set_flags(DB *db, uint32_t flags) {
+ HANDLE_PANICKED_DB(db);
+
+ /* the following matches BDB */
+ if (db_opened(db) && flags != 0) return EINVAL;
+
+ return 0;
+}
+
+static int
+toku_db_get_flags(DB *db, uint32_t *pflags) {
+ HANDLE_PANICKED_DB(db);
+ if (!pflags) return EINVAL;
+ *pflags = 0;
+ return 0;
+}
+
+static int
+toku_db_change_pagesize(DB *db, uint32_t pagesize) {
+ HANDLE_PANICKED_DB(db);
+ if (!db_opened(db)) return EINVAL;
+ toku_ft_handle_set_nodesize(db->i->ft_handle, pagesize);
+ return 0;
+}
+
+static int
+toku_db_set_pagesize(DB *db, uint32_t pagesize) {
+ HANDLE_PANICKED_DB(db);
+ if (db_opened(db)) return EINVAL;
+ toku_ft_handle_set_nodesize(db->i->ft_handle, pagesize);
+ return 0;
+}
+
+static int
+toku_db_get_pagesize(DB *db, uint32_t *pagesize_ptr) {
+ HANDLE_PANICKED_DB(db);
+ toku_ft_handle_get_nodesize(db->i->ft_handle, pagesize_ptr);
+ return 0;
+}
+
+static int
+toku_db_change_readpagesize(DB *db, uint32_t readpagesize) {
+ HANDLE_PANICKED_DB(db);
+ if (!db_opened(db)) return EINVAL;
+ toku_ft_handle_set_basementnodesize(db->i->ft_handle, readpagesize);
+ return 0;
+}
+
+static int
+toku_db_set_readpagesize(DB *db, uint32_t readpagesize) {
+ HANDLE_PANICKED_DB(db);
+ if (db_opened(db)) return EINVAL;
+ toku_ft_handle_set_basementnodesize(db->i->ft_handle, readpagesize);
+ return 0;
+}
+
+static int
+toku_db_get_readpagesize(DB *db, uint32_t *readpagesize_ptr) {
+ HANDLE_PANICKED_DB(db);
+ toku_ft_handle_get_basementnodesize(db->i->ft_handle, readpagesize_ptr);
+ return 0;
+}
+
+static int
+toku_db_change_compression_method(DB *db, enum toku_compression_method compression_method) {
+ HANDLE_PANICKED_DB(db);
+ if (!db_opened(db)) return EINVAL;
+ toku_ft_handle_set_compression_method(db->i->ft_handle, compression_method);
+ return 0;
+}
+
+static int
+toku_db_set_compression_method(DB *db, enum toku_compression_method compression_method) {
+ HANDLE_PANICKED_DB(db);
+ if (db_opened(db)) return EINVAL;
+ toku_ft_handle_set_compression_method(db->i->ft_handle, compression_method);
+ return 0;
+}
+
+static int
+toku_db_get_compression_method(DB *db, enum toku_compression_method *compression_method_ptr) {
+ HANDLE_PANICKED_DB(db);
+ toku_ft_handle_get_compression_method(db->i->ft_handle, compression_method_ptr);
+ return 0;
+}
+
+static int
+toku_db_change_fanout(DB *db, unsigned int fanout) {
+ HANDLE_PANICKED_DB(db);
+ if (!db_opened(db)) return EINVAL;
+ toku_ft_handle_set_fanout(db->i->ft_handle, fanout);
+ return 0;
+}
+
+static int
+toku_db_set_fanout(DB *db, unsigned int fanout) {
+ HANDLE_PANICKED_DB(db);
+ if (db_opened(db)) return EINVAL;
+ toku_ft_handle_set_fanout(db->i->ft_handle, fanout);
+ return 0;
+}
+
+static int
+toku_db_get_fanout(DB *db, unsigned int *fanout) {
+ HANDLE_PANICKED_DB(db);
+ toku_ft_handle_get_fanout(db->i->ft_handle, fanout);
+ return 0;
+}
+
+static int
+toku_db_set_memcmp_magic(DB *db, uint8_t magic) {
+ HANDLE_PANICKED_DB(db);
+ if (db_opened(db)) {
+ return EINVAL;
+ }
+ return toku_ft_handle_set_memcmp_magic(db->i->ft_handle, magic);
+}
+
+static int
+toku_db_get_fractal_tree_info64(DB *db, uint64_t *num_blocks_allocated, uint64_t *num_blocks_in_use, uint64_t *size_allocated, uint64_t *size_in_use) {
+ HANDLE_PANICKED_DB(db);
+ struct ftinfo64 ftinfo;
+ toku_ft_handle_get_fractal_tree_info64(db->i->ft_handle, &ftinfo);
+ *num_blocks_allocated = ftinfo.num_blocks_allocated;
+ *num_blocks_in_use = ftinfo.num_blocks_in_use;
+ *size_allocated = ftinfo.size_allocated;
+ *size_in_use = ftinfo.size_in_use;
+ return 0;
+}
+
+static int
+toku_db_iterate_fractal_tree_block_map(DB *db, int (*iter)(uint64_t,int64_t,int64_t,int64_t,int64_t,void*), void *iter_extra) {
+ HANDLE_PANICKED_DB(db);
+ return toku_ft_handle_iterate_fractal_tree_block_map(db->i->ft_handle, iter, iter_extra);
+}
+
+static int
+toku_db_stat64(DB * db, DB_TXN *txn, DB_BTREE_STAT64 *s) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ struct ftstat64_s ftstat;
+ TOKUTXN tokutxn = NULL;
+ if (txn != NULL) {
+ tokutxn = db_txn_struct_i(txn)->tokutxn;
+ }
+ toku_ft_handle_stat64(db->i->ft_handle, tokutxn, &ftstat);
+ s->bt_nkeys = ftstat.nkeys;
+ s->bt_ndata = ftstat.ndata;
+ s->bt_dsize = ftstat.dsize;
+ s->bt_fsize = ftstat.fsize;
+ s->bt_create_time_sec = ftstat.create_time_sec;
+ s->bt_modify_time_sec = ftstat.modify_time_sec;
+ s->bt_verify_time_sec = ftstat.verify_time_sec;
+ return 0;
+}
+
+static const char *
+toku_db_get_dname(DB *db) {
+ if (!db_opened(db)) {
+ return nullptr;
+ }
+ if (db->i->dname == nullptr) {
+ return "";
+ }
+ return db->i->dname;
+}
+
+static int
+toku_db_keys_range64(DB* db, DB_TXN* txn __attribute__((__unused__)), DBT* keyleft, DBT* keyright, uint64_t* less, uint64_t* left, uint64_t* between, uint64_t *right, uint64_t *greater, bool* middle_3_exact) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+
+ // note that we ignore the txn param. It would be more complicated to support it.
+ // TODO(yoni): Maybe add support for txns later? How would we do this? ydb lock comment about db_keyrange64 is obsolete.
+ toku_ft_keysrange(db->i->ft_handle, keyleft, keyright, less, left, between, right, greater, middle_3_exact);
+ return 0;
+}
+
+static int
+toku_db_key_range64(DB* db, DB_TXN* txn, DBT* key, uint64_t* less_p, uint64_t* equal_p, uint64_t* greater_p, int* is_exact) {
+ uint64_t less, equal_left, middle, equal_right, greater;
+ bool ignore;
+ int r = toku_db_keys_range64(db, txn, key, NULL, &less, &equal_left, &middle, &equal_right, &greater, &ignore);
+ if (r == 0) {
+ *less_p = less;
+ *equal_p = equal_left;
+ *greater_p = middle;
+ paranoid_invariant_zero(greater); // no keys are greater than positive infinity
+ paranoid_invariant_zero(equal_right); // no keys are equal to positive infinity
+ // toku_ft_keysrange does not know when all 3 are exact, so set is_exact to false
+ *is_exact = false;
+ }
+ return 0;
+}
+
+static int toku_db_get_key_after_bytes(DB *db, DB_TXN *txn, const DBT *start_key, uint64_t skip_len, void (*callback)(const DBT *end_key, uint64_t actually_skipped, void *extra), void *cb_extra, uint32_t UU(flags)) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ return toku_ft_get_key_after_bytes(db->i->ft_handle, start_key, skip_len, callback, cb_extra);
+}
+
+// needed by loader.c
+int
+toku_db_pre_acquire_table_lock(DB *db, DB_TXN *txn) {
+ HANDLE_PANICKED_DB(db);
+ if (!db->i->lt || !txn) return 0;
+ int r;
+ r = toku_db_get_range_lock(db, txn,
+ toku_dbt_negative_infinity(), toku_dbt_positive_infinity(),
+ toku::lock_request::type::WRITE);
+ return r;
+}
+
+static int
+locked_db_close(DB * db, uint32_t UU(flags)) {
+ // cannot begin a checkpoint
+ toku_multi_operation_client_lock();
+ int r = toku_db_close(db);
+ toku_multi_operation_client_unlock();
+ return r;
+}
+
+int
+autotxn_db_get(DB* db, DB_TXN* txn, DBT* key, DBT* data, uint32_t flags) {
+ bool changed; int r;
+ r = toku_db_construct_autotxn(db, &txn, &changed, false);
+ if (r!=0) return r;
+ r = toku_db_get(db, txn, key, data, flags);
+ return toku_db_destruct_autotxn(txn, r, changed);
+}
+
+static inline int
+autotxn_db_getf_set (DB *db, DB_TXN *txn, uint32_t flags, DBT *key, YDB_CALLBACK_FUNCTION f, void *extra) {
+ bool changed; int r;
+ r = toku_db_construct_autotxn(db, &txn, &changed, false);
+ if (r!=0) return r;
+ r = db_getf_set(db, txn, flags, key, f, extra);
+ return toku_db_destruct_autotxn(txn, r, changed);
+}
+
+static int
+locked_db_open(DB *db, DB_TXN *txn, const char *fname, const char *dbname, DBTYPE dbtype, uint32_t flags, int mode) {
+ int ret, r;
+ HANDLE_READ_ONLY_TXN(txn);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+
+ //
+ // Note that this function opens a db with a transaction. Should
+ // the transaction abort, the user is responsible for closing the DB
+ // before aborting the transaction. Not doing so results in undefined
+ // behavior.
+ //
+ DB_ENV *env = db->dbenv;
+ DB_TXN *child_txn = NULL;
+ int using_txns = env->i->open_flags & DB_INIT_TXN;
+ if (using_txns) {
+ ret = toku_txn_begin(env, txn, &child_txn, DB_TXN_NOSYNC);
+ invariant_zero(ret);
+ }
+
+ // cannot begin a checkpoint
+ toku_multi_operation_client_lock();
+ r = toku_db_open(db, child_txn, fname, dbname, dbtype, flags & ~DB_AUTO_COMMIT, mode);
+ toku_multi_operation_client_unlock();
+
+ if (using_txns) {
+ if (r == 0) {
+ ret = locked_txn_commit(child_txn, DB_TXN_NOSYNC);
+ invariant_zero(ret);
+ } else {
+ ret = locked_txn_abort(child_txn);
+ invariant_zero(ret);
+ }
+ }
+ return r;
+}
+
+static int
+locked_db_change_descriptor(DB *db, DB_TXN *txn, const DBT *descriptor, uint32_t flags) {
+ // cannot begin a checkpoint
+ toku_multi_operation_client_lock();
+ int r = toku_db_change_descriptor(db, txn, descriptor, flags);
+ toku_multi_operation_client_unlock();
+ return r;
+}
+
+static int
+autotxn_db_change_descriptor(DB *db, DB_TXN *txn, const DBT *descriptor, uint32_t flags) {
+ bool changed; int r;
+ r = toku_db_construct_autotxn(db, &txn, &changed, false);
+ if (r != 0) { return r; }
+ r = locked_db_change_descriptor(db, txn, descriptor, flags);
+ return toku_db_destruct_autotxn(txn, r, changed);
+}
+
+static void
+toku_db_set_errfile (DB *db, FILE *errfile) {
+ db->dbenv->set_errfile(db->dbenv, errfile);
+}
+
+// TODO 2216 delete this
+static int
+toku_db_fd(DB * UU(db), int * UU(fdp)) {
+ return 0;
+}
+
+static const DBT* toku_db_dbt_pos_infty(void) __attribute__((pure));
+static const DBT*
+toku_db_dbt_pos_infty(void) {
+ return toku_dbt_positive_infinity();
+}
+
+static const DBT* toku_db_dbt_neg_infty(void) __attribute__((pure));
+static const DBT*
+toku_db_dbt_neg_infty(void) {
+ return toku_dbt_negative_infinity();
+}
+
+static int
+toku_db_optimize(DB *db) {
+ HANDLE_PANICKED_DB(db);
+ toku_ft_optimize(db->i->ft_handle);
+ return 0;
+}
+
+static int
+toku_db_hot_optimize(DB *db, DBT* left, DBT* right,
+ int (*progress_callback)(void *extra, float progress),
+ void *progress_extra, uint64_t* loops_run)
+{
+ HANDLE_PANICKED_DB(db);
+ int r = 0;
+ r = toku_ft_hot_optimize(db->i->ft_handle, left, right,
+ progress_callback,
+ progress_extra, loops_run);
+
+ return r;
+}
+
+static int
+locked_db_optimize(DB *db) {
+ // need to protect from checkpointing because
+ // toku_db_optimize does a message injection
+ toku_multi_operation_client_lock(); //Cannot begin checkpoint
+ int r = toku_db_optimize(db);
+ toku_multi_operation_client_unlock();
+ return r;
+}
+
+
+struct last_key_extra {
+ YDB_CALLBACK_FUNCTION func;
+ void* extra;
+};
+
+static int
+db_get_last_key_callback(uint32_t keylen, const void *key, uint32_t vallen UU(), const void *val UU(), void *extra, bool lock_only) {
+ if (!lock_only) {
+ DBT keydbt;
+ toku_fill_dbt(&keydbt, key, keylen);
+ struct last_key_extra * CAST_FROM_VOIDP(info, extra);
+ info->func(&keydbt, NULL, info->extra);
+ }
+ return 0;
+}
+
+static int
+toku_db_get_last_key(DB * db, DB_TXN *txn, YDB_CALLBACK_FUNCTION func, void* extra) {
+ int r;
+ LE_CURSOR cursor = nullptr;
+ struct last_key_extra last_extra = { .func = func, .extra = extra };
+
+ r = toku_le_cursor_create(&cursor, db->i->ft_handle, db_txn_struct_i(txn)->tokutxn);
+ if (r != 0) { goto cleanup; }
+
+ // Goes in reverse order. First key returned is last in dictionary.
+ r = toku_le_cursor_next(cursor, db_get_last_key_callback, &last_extra);
+ if (r != 0) { goto cleanup; }
+
+cleanup:
+ if (cursor) {
+ toku_le_cursor_close(cursor);
+ }
+ return r;
+}
+
+static int
+autotxn_db_get_last_key(DB* db, YDB_CALLBACK_FUNCTION func, void* extra) {
+ bool changed; int r;
+ DB_TXN *txn = nullptr;
+ // Cursors inside require transactions, but this is _not_ a transactional function.
+ // Create transaction in a wrapper and then later close it.
+ r = toku_db_construct_autotxn(db, &txn, &changed, false);
+ if (r!=0) return r;
+ r = toku_db_get_last_key(db, txn, func, extra);
+ return toku_db_destruct_autotxn(txn, r, changed);
+}
+
+static int
+toku_db_get_fragmentation(DB * db, TOKU_DB_FRAGMENTATION report) {
+ HANDLE_PANICKED_DB(db);
+ int r;
+ if (!db_opened(db))
+ r = toku_ydb_do_error(db->dbenv, EINVAL, "Fragmentation report available only on open DBs.\n");
+ else
+ r = toku_ft_get_fragmentation(db->i->ft_handle, report);
+ return r;
+}
+
+int
+toku_db_set_indexer(DB *db, DB_INDEXER * indexer) {
+ int r = 0;
+ if ( db->i->indexer != NULL && indexer != NULL ) {
+ // you are trying to overwrite a valid indexer
+ r = EINVAL;
+ }
+ else {
+ db->i->indexer = indexer;
+ }
+ return r;
+}
+
+DB_INDEXER *
+toku_db_get_indexer(DB *db) {
+ return db->i->indexer;
+}
+
+static void
+db_get_indexer(DB *db, DB_INDEXER **indexer_ptr) {
+ *indexer_ptr = toku_db_get_indexer(db);
+}
+
+struct ydb_verify_context {
+ int (*progress_callback)(void *extra, float progress);
+ void *progress_extra;
+};
+
+static int
+ydb_verify_progress_callback(void *extra, float progress) {
+ struct ydb_verify_context *context = (struct ydb_verify_context *) extra;
+ int r = 0;
+ if (context->progress_callback) {
+ r = context->progress_callback(context->progress_extra, progress);
+ }
+ return r;
+}
+
+static int
+toku_db_verify_with_progress(DB *db, int (*progress_callback)(void *extra, float progress), void *progress_extra, int verbose, int keep_going) {
+ struct ydb_verify_context context = { progress_callback, progress_extra };
+ int r = toku_verify_ft_with_progress(db->i->ft_handle, ydb_verify_progress_callback, &context, verbose, keep_going);
+ return r;
+}
+
+int toku_setup_db_internal (DB **dbp, DB_ENV *env, uint32_t flags, FT_HANDLE ft_handle, bool is_open) {
+ if (flags || env == NULL)
+ return EINVAL;
+
+ if (!env_opened(env))
+ return EINVAL;
+
+ DB *MALLOC(result);
+ if (result == 0) {
+ return ENOMEM;
+ }
+ memset(result, 0, sizeof *result);
+ result->dbenv = env;
+ MALLOC(result->i);
+ if (result->i == 0) {
+ toku_free(result);
+ return ENOMEM;
+ }
+ memset(result->i, 0, sizeof *result->i);
+ result->i->ft_handle = ft_handle;
+ result->i->opened = is_open;
+ *dbp = result;
+ return 0;
+}
+
+int
+toku_db_create(DB ** db, DB_ENV * env, uint32_t flags) {
+ if (flags || env == NULL)
+ return EINVAL;
+
+ if (!env_opened(env))
+ return EINVAL;
+
+
+ FT_HANDLE ft_handle;
+ toku_ft_handle_create(&ft_handle);
+
+ int r = toku_setup_db_internal(db, env, flags, ft_handle, false);
+ if (r != 0) return r;
+
+ DB *result=*db;
+ // methods that grab the ydb lock
+#define SDB(name) result->name = locked_db_ ## name
+ SDB(close);
+ SDB(open);
+ SDB(optimize);
+#undef SDB
+ // methods that do not take the ydb lock
+#define USDB(name) result->name = toku_db_ ## name
+ USDB(set_errfile);
+ USDB(set_pagesize);
+ USDB(get_pagesize);
+ USDB(change_pagesize);
+ USDB(set_readpagesize);
+ USDB(get_readpagesize);
+ USDB(change_readpagesize);
+ USDB(set_compression_method);
+ USDB(get_compression_method);
+ USDB(change_compression_method);
+ USDB(set_fanout);
+ USDB(get_fanout);
+ USDB(set_memcmp_magic);
+ USDB(change_fanout);
+ USDB(set_flags);
+ USDB(get_flags);
+ USDB(fd);
+ USDB(get_max_row_size);
+ USDB(set_indexer);
+ USDB(pre_acquire_table_lock);
+ USDB(pre_acquire_fileops_lock);
+ USDB(key_range64);
+ USDB(keys_range64);
+ USDB(get_key_after_bytes);
+ USDB(hot_optimize);
+ USDB(stat64);
+ USDB(get_fractal_tree_info64);
+ USDB(iterate_fractal_tree_block_map);
+ USDB(get_dname);
+ USDB(verify_with_progress);
+ USDB(cursor);
+ USDB(dbt_pos_infty);
+ USDB(dbt_neg_infty);
+ USDB(get_fragmentation);
+#undef USDB
+ result->get_indexer = db_get_indexer;
+ result->del = autotxn_db_del;
+ result->put = autotxn_db_put;
+ result->update = autotxn_db_update;
+ result->update_broadcast = autotxn_db_update_broadcast;
+ result->change_descriptor = autotxn_db_change_descriptor;
+ result->get_last_key = autotxn_db_get_last_key;
+
+ // unlocked methods
+ result->get = autotxn_db_get;
+ result->getf_set = autotxn_db_getf_set;
+
+ result->i->dict_id = DICTIONARY_ID_NONE;
+ result->i->opened = 0;
+ result->i->open_flags = 0;
+ result->i->open_mode = 0;
+ result->i->indexer = NULL;
+ *db = result;
+ return 0;
+}
+
+// When the loader is created, it makes this call (toku_env_load_inames).
+// For each dictionary to be loaded, replace old iname in directory
+// with a newly generated iname. This will also take a write lock
+// on the directory entries. The write lock will be released when
+// the transaction of the loader is completed.
+// If the transaction commits, the new inames are in place.
+// If the transaction aborts, the old inames will be restored.
+// The new inames are returned to the caller.
+// It is the caller's responsibility to free them.
+// If "mark_as_loader" is true, then include a mark in the iname
+// to indicate that the file is created by the ft loader.
+// Return 0 on success (could fail if write lock not available).
+static int
+load_inames(DB_ENV * env, DB_TXN * txn, int N, DB * dbs[/*N*/], const char * new_inames_in_env[/*N*/], LSN *load_lsn, bool mark_as_loader) {
+ int rval = 0;
+ int i;
+
+ TXNID_PAIR xid = TXNID_PAIR_NONE;
+ DBT dname_dbt; // holds dname
+ DBT iname_dbt; // holds new iname
+
+ const char *mark;
+
+ if (mark_as_loader) {
+ mark = "B";
+ } else {
+ mark = "P";
+ }
+
+ for (i=0; i<N; i++) {
+ new_inames_in_env[i] = NULL;
+ }
+
+ if (txn) {
+ xid = toku_txn_get_txnid(db_txn_struct_i(txn)->tokutxn);
+ }
+ for (i = 0; i < N; i++) {
+ char * dname = dbs[i]->i->dname;
+ toku_fill_dbt(&dname_dbt, dname, strlen(dname)+1);
+ // now create new iname
+ char hint[strlen(dname) + 1];
+ create_iname_hint(dname, hint);
+ const char *new_iname = create_iname(env, xid.parent_id64, xid.child_id64, hint, mark, i); // allocates memory for iname_in_env
+ new_inames_in_env[i] = new_iname;
+ toku_fill_dbt(&iname_dbt, new_iname, strlen(new_iname) + 1); // iname_in_env goes in directory
+ rval = toku_db_put(env->i->directory, txn, &dname_dbt, &iname_dbt, 0, true);
+ if (rval) break;
+ }
+
+ // Generate load log entries.
+ if (!rval && txn) {
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ int do_fsync = 0;
+ LSN *get_lsn = NULL;
+ for (i = 0; i < N; i++) {
+ FT_HANDLE ft_handle = dbs[i]->i->ft_handle;
+ //Fsync is necessary for the last one only.
+ if (i==N-1) {
+ do_fsync = 1; //We only need a single fsync of logs.
+ get_lsn = load_lsn; //Set pointer to capture the last lsn.
+ }
+ toku_ft_load(ft_handle, ttxn, new_inames_in_env[i], do_fsync, get_lsn);
+ }
+ }
+ return rval;
+}
+
+int
+locked_load_inames(DB_ENV * env, DB_TXN * txn, int N, DB * dbs[/*N*/], char * new_inames_in_env[/*N*/], LSN *load_lsn, bool mark_as_loader) {
+ int r;
+ HANDLE_READ_ONLY_TXN(txn);
+
+ // cannot begin a checkpoint
+ toku_multi_operation_client_lock();
+ r = load_inames(env, txn, N, dbs, (const char **) new_inames_in_env, load_lsn, mark_as_loader);
+ toku_multi_operation_client_unlock();
+
+ return r;
+
+}
+
+#undef STATUS_VALUE
+
+#include <toku_race_tools.h>
+void __attribute__((constructor)) toku_ydb_db_helgrind_ignore(void);
+void
+toku_ydb_db_helgrind_ignore(void) {
+ TOKU_VALGRIND_HG_DISABLE_CHECKING(&ydb_db_layer_status, sizeof ydb_db_layer_status);
+}
diff --git a/storage/tokudb/PerconaFT/src/ydb_db.h b/storage/tokudb/PerconaFT/src/ydb_db.h
new file mode 100644
index 00000000000..8b92dd1c3cb
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_db.h
@@ -0,0 +1,121 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+#include <ft/ft.h>
+
+#include "ydb-internal.h"
+#include "ydb_txn.h"
+
+typedef enum {
+ YDB_LAYER_DIRECTORY_WRITE_LOCKS = 0, /* total directory write locks taken */
+ YDB_LAYER_DIRECTORY_WRITE_LOCKS_FAIL, /* total directory write locks unable to be taken */
+ YDB_LAYER_LOGSUPPRESS, /* number of times logs are suppressed for empty table (2440) */
+ YDB_LAYER_LOGSUPPRESS_FAIL, /* number of times unable to suppress logs for empty table (2440) */
+ YDB_DB_LAYER_STATUS_NUM_ROWS /* number of rows in this status array */
+} ydb_db_lock_layer_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[YDB_DB_LAYER_STATUS_NUM_ROWS];
+} YDB_DB_LAYER_STATUS_S, *YDB_DB_LAYER_STATUS;
+
+void ydb_db_layer_get_status(YDB_DB_LAYER_STATUS statp);
+
+//
+// export the following locktree create/destroy callbacks so
+// the environment can pass them to the locktree manager.
+//
+struct lt_on_create_callback_extra {
+ DB_TXN *txn;
+ FT_HANDLE ft_handle;
+};
+int toku_db_lt_on_create_callback(toku::locktree *lt, void *extra);
+void toku_db_lt_on_destroy_callback(toku::locktree *lt);
+
+/* db methods */
+static inline int db_opened(DB *db) {
+ return db->i->opened != 0;
+}
+
+static inline const toku::comparator &toku_db_get_comparator(DB *db) {
+ return toku_ft_get_comparator(db->i->ft_handle);
+}
+
+int toku_db_use_builtin_key_cmp(DB *db);
+int toku_db_pre_acquire_fileops_lock(DB *db, DB_TXN *txn);
+int toku_db_open_iname(DB * db, DB_TXN * txn, const char *iname, uint32_t flags, int mode);
+int toku_db_pre_acquire_table_lock(DB *db, DB_TXN *txn);
+int toku_db_get (DB * db, DB_TXN * txn, DBT * key, DBT * data, uint32_t flags);
+int toku_db_create(DB ** db, DB_ENV * env, uint32_t flags);
+int toku_db_close(DB * db);
+int toku_setup_db_internal (DB **dbp, DB_ENV *env, uint32_t flags, FT_HANDLE ft_handle, bool is_open);
+int db_getf_set(DB *db, DB_TXN *txn, uint32_t flags, DBT *key, YDB_CALLBACK_FUNCTION f, void *extra);
+int autotxn_db_get(DB* db, DB_TXN* txn, DBT* key, DBT* data, uint32_t flags);
+
+//TODO: DB_AUTO_COMMIT.
+//TODO: Nowait only conditionally?
+//TODO: NOSYNC change to SYNC if DB_ENV has something in set_flags
+static inline int
+toku_db_construct_autotxn(DB* db, DB_TXN **txn, bool* changed, bool force_auto_commit) {
+ assert(db && txn && changed);
+ DB_ENV* env = db->dbenv;
+ if (*txn || !(env->i->open_flags & DB_INIT_TXN)) {
+ *changed = false;
+ return 0;
+ }
+ bool nosync = (bool)(!force_auto_commit && !(env->i->open_flags & DB_AUTO_COMMIT));
+ uint32_t txn_flags = DB_TXN_NOWAIT | (nosync ? DB_TXN_NOSYNC : 0);
+ int r = toku_txn_begin(env, NULL, txn, txn_flags);
+ if (r!=0) return r;
+ *changed = true;
+ return 0;
+}
+
+static inline int
+toku_db_destruct_autotxn(DB_TXN *txn, int r, bool changed) {
+ if (!changed) return r;
+ if (r==0) {
+ r = locked_txn_commit(txn, 0);
+ }
+ else {
+ locked_txn_abort(txn);
+ }
+ return r;
+}
diff --git a/storage/tokudb/PerconaFT/src/ydb_env_func.cc b/storage/tokudb/PerconaFT/src/ydb_env_func.cc
new file mode 100644
index 00000000000..13c56fec6bf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_env_func.cc
@@ -0,0 +1,184 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <toku_portability.h>
+
+#include <memory.h>
+#include <db.h>
+
+#include <ft/cachetable/checkpoint.h>
+#include <ft/ft.h>
+#include <ft/ft-ops.h>
+#include <ft/ft-flusher.h>
+#include <ft/logger/recover.h>
+#include <ft/loader/loader.h>
+
+#include "ydb_env_func.h"
+
+// For test purposes only.
+// These callbacks are never used in production code, only as a way to test the system
+// (for example, by causing crashes at predictable times).
+void (*checkpoint_callback_f)(void*) = NULL;
+void * checkpoint_callback_extra = NULL;
+void (*checkpoint_callback2_f)(void*) = NULL;
+void * checkpoint_callback2_extra = NULL;
+
+bool engine_status_enable = true; // if false, suppress engine status output on failed assert, for test programs only
+
+void db_env_set_direct_io (bool direct_io_on) {
+ toku_ft_set_direct_io(direct_io_on);
+}
+
+void db_env_set_compress_buffers_before_eviction (bool compress_buffers) {
+ toku_ft_set_compress_buffers_before_eviction(compress_buffers);
+}
+
+void db_env_set_func_fsync (int (*fsync_function)(int)) {
+ toku_set_func_fsync(fsync_function);
+}
+
+void db_env_set_func_pwrite (ssize_t (*pwrite_function)(int, const void *, size_t, toku_off_t)) {
+ toku_set_func_pwrite(pwrite_function);
+}
+
+void db_env_set_func_full_pwrite (ssize_t (*pwrite_function)(int, const void *, size_t, toku_off_t)) {
+ toku_set_func_full_pwrite(pwrite_function);
+}
+
+void db_env_set_func_write (ssize_t (*write_function)(int, const void *, size_t)) {
+ toku_set_func_write(write_function);
+}
+
+void db_env_set_func_full_write (ssize_t (*write_function)(int, const void *, size_t)) {
+ toku_set_func_full_write(write_function);
+}
+
+void db_env_set_func_fdopen (FILE * (*fdopen_function)(int, const char *)) {
+ toku_set_func_fdopen(fdopen_function);
+}
+
+void db_env_set_func_fopen (FILE * (*fopen_function)(const char *, const char *)) {
+ toku_set_func_fopen(fopen_function);
+}
+
+void db_env_set_func_open (int (*open_function)(const char *, int, int)) {
+ toku_set_func_open(open_function);
+}
+
+void db_env_set_func_fclose (int (*fclose_function)(FILE*)) {
+ toku_set_func_fclose(fclose_function);
+}
+
+void db_env_set_func_pread (ssize_t (*fun)(int, void *, size_t, off_t)) {
+ toku_set_func_pread(fun);
+}
+
+void db_env_set_func_loader_fwrite (size_t (*fwrite_fun)(const void*,size_t,size_t,FILE*)) {
+ ft_loader_set_os_fwrite(fwrite_fun);
+}
+
+void db_env_set_func_malloc (void *(*f)(size_t)) {
+ toku_set_func_malloc(f);
+}
+
+void db_env_set_func_realloc (void *(*f)(void*, size_t)) {
+ toku_set_func_realloc(f);
+}
+
+void db_env_set_func_free (void (*f)(void*)) {
+ toku_set_func_free(f);
+}
+
+// For test purposes only.
+// With this interface, all checkpoint users get the same callbacks and the same extras.
+void
+db_env_set_checkpoint_callback (void (*callback_f)(void*), void* extra) {
+ toku_checkpoint_safe_client_lock();
+ checkpoint_callback_f = callback_f;
+ checkpoint_callback_extra = extra;
+ toku_checkpoint_safe_client_unlock();
+}
+
+void
+db_env_set_checkpoint_callback2 (void (*callback_f)(void*), void* extra) {
+ toku_checkpoint_safe_client_lock();
+ checkpoint_callback2_f = callback_f;
+ checkpoint_callback2_extra = extra;
+ toku_checkpoint_safe_client_unlock();
+}
+
+void
+db_env_set_recover_callback (void (*callback_f)(void*), void* extra) {
+ toku_recover_set_callback(callback_f, extra);
+}
+
+void
+db_env_set_recover_callback2 (void (*callback_f)(void*), void* extra) {
+ toku_recover_set_callback2(callback_f, extra);
+}
+
+void
+db_env_set_flusher_thread_callback(void (*callback_f)(int, void*), void* extra) {
+ toku_flusher_thread_set_callback(callback_f, extra);
+}
+
+void
+db_env_set_loader_size_factor (uint32_t factor) {
+ toku_ft_loader_set_size_factor(factor);
+}
+
+void
+db_env_set_mvcc_garbage_collection_verification(uint32_t verification_mode) {
+ garbage_collection_debug = (verification_mode != 0);
+}
+
+// Purpose: allow test programs that expect to fail to suppress engine status output on failed assert.
+void
+db_env_enable_engine_status(bool enable) {
+ engine_status_enable = enable;
+}
+
+void
+db_env_set_num_bucket_mutexes(uint32_t num_mutexes) {
+ toku_pair_list_set_lock_size(num_mutexes);
+}
+
+void db_env_try_gdb_stack_trace(const char *gdb_path) {
+ toku_try_gdb_stack_trace(gdb_path);
+}
+
diff --git a/storage/tokudb/PerconaFT/src/ydb_env_func.h b/storage/tokudb/PerconaFT/src/ydb_env_func.h
new file mode 100644
index 00000000000..e863c2d4668
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_env_func.h
@@ -0,0 +1,52 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+extern void (*checkpoint_callback_f)(void*);
+extern void * checkpoint_callback_extra;
+extern void (*checkpoint_callback2_f)(void*);
+extern void * checkpoint_callback2_extra;
+
+extern bool engine_status_enable;
+
+// Called to use dlmalloc functions.
+void setup_dlmalloc(void) __attribute__((__visibility__("default")));
+
+// Test-only function
+void toku_env_increase_last_xid(DB_ENV *env, uint64_t increment);
diff --git a/storage/tokudb/PerconaFT/src/ydb_lib.cc b/storage/tokudb/PerconaFT/src/ydb_lib.cc
new file mode 100644
index 00000000000..bf3cde4e927
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_lib.cc
@@ -0,0 +1,57 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <stdio.h>
+#include <toku_stdint.h>
+#include <toku_portability.h>
+#include <db.h>
+#include "ydb.h"
+#include <toku_assert.h>
+
+#if defined(__GNUC__)
+
+static void __attribute__((constructor)) libtokuft_init(void) {
+ int r = toku_ydb_init();
+ assert(r==0);
+}
+
+static void __attribute__((destructor)) libtokuft_destroy(void) {
+ toku_ydb_destroy();
+}
+
+#endif // __GNUC__
diff --git a/storage/tokudb/PerconaFT/src/ydb_load.h b/storage/tokudb/PerconaFT/src/ydb_load.h
new file mode 100644
index 00000000000..aa253ab42ed
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_load.h
@@ -0,0 +1,62 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+/* ydb functions used by loader
+ */
+
+// When the loader is created, it makes this call.
+// For each dictionary to be loaded, replace old iname in directory
+// with a newly generated iname. This will also take a write lock
+// on the directory entries. The write lock will be released when
+// the transaction of the loader is completed.
+// If the transaction commits, the new inames are in place.
+// If the transaction aborts, the old inames will be restored.
+// The new inames are returned to the caller.
+// It is the caller's responsibility to free them.
+// If "mark_as_loader" is true, then include a mark in the iname
+// to indicate that the file is created by the ft loader.
+// Return 0 on success (could fail if write lock not available).
+int locked_load_inames(DB_ENV * env,
+ DB_TXN * txn,
+ int N,
+ DB * dbs[/*N*/],
+ char * new_inames_in_env[/*N*/], /* out */
+ LSN *load_lsn,
+ bool mark_as_loader);
diff --git a/storage/tokudb/PerconaFT/src/ydb_row_lock.cc b/storage/tokudb/PerconaFT/src/ydb_row_lock.cc
new file mode 100644
index 00000000000..913e1a44faf
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_row_lock.cc
@@ -0,0 +1,276 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <db.h>
+
+#include <locktree/lock_request.h>
+
+#include "ydb-internal.h"
+#include "ydb_txn.h"
+#include "ydb_row_lock.h"
+
+/*
+ Used for partial implementation of nested transactions.
+ Work is done by children as normal, but all locking is done by the
+ root of the nested txn tree.
+ This may hold extra locks, and will not work as expected when
+ a node has two non-completed txns at any time.
+*/
+static DB_TXN *txn_oldest_ancester(DB_TXN* txn) {
+ while (txn && txn->parent) {
+ txn = txn->parent;
+ }
+ return txn;
+}
+
+int find_key_ranges_by_lt(const txn_lt_key_ranges &ranges,
+ const toku::locktree *const &find_lt);
+int find_key_ranges_by_lt(const txn_lt_key_ranges &ranges,
+ const toku::locktree *const &find_lt) {
+ return ranges.lt->compare(find_lt);
+}
+
+static void db_txn_note_row_lock(DB *db, DB_TXN *txn, const DBT *left_key, const DBT *right_key) {
+ const toku::locktree *lt = db->i->lt;
+
+ toku_mutex_lock(&db_txn_struct_i(txn)->txn_mutex);
+
+ uint32_t idx;
+ txn_lt_key_ranges ranges;
+ toku::omt<txn_lt_key_ranges> *map = &db_txn_struct_i(txn)->lt_map;
+
+ // if this txn has not yet already referenced this
+ // locktree, then add it to this txn's locktree map
+ int r = map->find_zero<const toku::locktree *, find_key_ranges_by_lt>(lt, &ranges, &idx);
+ if (r == DB_NOTFOUND) {
+ ranges.lt = db->i->lt;
+ XMALLOC(ranges.buffer);
+ ranges.buffer->create();
+ map->insert_at(ranges, idx);
+
+ // let the manager know we're referencing this lt
+ toku::locktree_manager *ltm = &txn->mgrp->i->ltm;
+ ltm->reference_lt(ranges.lt);
+ } else {
+ invariant_zero(r);
+ }
+
+ // add a new lock range to this txn's row lock buffer
+ size_t old_mem_size = ranges.buffer->total_memory_size();
+ ranges.buffer->append(left_key, right_key);
+ size_t new_mem_size = ranges.buffer->total_memory_size();
+ invariant(new_mem_size > old_mem_size);
+ lt->get_manager()->note_mem_used(new_mem_size - old_mem_size);
+
+ toku_mutex_unlock(&db_txn_struct_i(txn)->txn_mutex);
+}
+
+void toku_db_txn_escalate_callback(TXNID txnid, const toku::locktree *lt, const toku::range_buffer &buffer, void *extra) {
+ DB_ENV *CAST_FROM_VOIDP(env, extra);
+
+ // Get the TOKUTXN and DB_TXN for this txnid from the environment's txn manager.
+ // Only the parent id is used in the search.
+ TOKUTXN ttxn;
+ TXNID_PAIR txnid_pair = { .parent_id64 = txnid, .child_id64 = 0 };
+ TXN_MANAGER txn_manager = toku_logger_get_txn_manager(env->i->logger);
+
+ toku_txn_manager_suspend(txn_manager);
+ toku_txn_manager_id2txn_unlocked(txn_manager, txnid_pair, &ttxn);
+
+ // We are still holding the txn manager lock. If we couldn't find the txn,
+ // then we lost a race with a committing transaction that got removed
+ // from the txn manager before it released its locktree locks. In this
+ // case we do nothing - that transaction has or is just about to release
+ // its locks and be gone, so there's not point in updating its lt_map
+ // with the new escalated ranges. It will go about releasing the old
+ // locks it thinks it had, and will succeed as if nothing happened.
+ //
+ // If we did find the transaction, then it has not yet been removed
+ // from the manager and therefore has not yet released its locks.
+ // We must try to replace the range buffer associated with this locktree,
+ // if it exists. This is impotant, otherwise it can grow out of
+ // control (ticket 5961).
+
+ if (ttxn != nullptr) {
+ DB_TXN *txn = toku_txn_get_container_db_txn(ttxn);
+
+ // One subtle point is that if the transaction is still live, it is impossible
+ // to deadlock on the txn mutex, even though we are holding the locktree's root
+ // mutex and release locks takes them in the opposite order.
+ //
+ // Proof: releasing locks takes the txn mutex and then acquires the locktree's
+ // root mutex, escalation takes the root mutex and possibly takes the txn mutex.
+ // releasing locks implies the txn is not live, and a non-live txn implies we
+ // will not need to take the txn mutex, so the deadlock is avoided.
+ toku_mutex_lock(&db_txn_struct_i(txn)->txn_mutex);
+
+ uint32_t idx;
+ txn_lt_key_ranges ranges;
+ toku::omt<txn_lt_key_ranges> *map = &db_txn_struct_i(txn)->lt_map;
+ int r = map->find_zero<const toku::locktree *, find_key_ranges_by_lt>(lt, &ranges, &idx);
+ if (r == 0) {
+ // Destroy the old range buffer, create a new one, and insert the new ranges.
+ //
+ // We could theoretically steal the memory from the caller instead of copying
+ // it, but it's simpler to have a callback API that doesn't transfer memory ownership.
+ lt->get_manager()->note_mem_released(ranges.buffer->total_memory_size());
+ ranges.buffer->destroy();
+ ranges.buffer->create();
+ toku::range_buffer::iterator iter(&buffer);
+ toku::range_buffer::iterator::record rec;
+ while (iter.current(&rec)) {
+ ranges.buffer->append(rec.get_left_key(), rec.get_right_key());
+ iter.next();
+ }
+ lt->get_manager()->note_mem_used(ranges.buffer->total_memory_size());
+ } else {
+ // In rare cases, we may not find the associated locktree, because we are
+ // racing with the transaction trying to add this locktree to the lt map
+ // after acquiring its first lock. The escalated lock set must be the single
+ // lock that this txnid just acquired. Do nothing here and let the txn
+ // take care of adding this locktree and range to its lt map as usual.
+ invariant(buffer.get_num_ranges() == 1);
+ }
+
+ toku_mutex_unlock(&db_txn_struct_i(txn)->txn_mutex);
+ }
+
+ toku_txn_manager_resume(txn_manager);
+}
+
+// Get a range lock.
+// Return when the range lock is acquired or the default lock tree timeout has expired.
+int toku_db_get_range_lock(DB *db, DB_TXN *txn, const DBT *left_key, const DBT *right_key,
+ toku::lock_request::type lock_type) {
+ toku::lock_request request;
+ request.create();
+ int r = toku_db_start_range_lock(db, txn, left_key, right_key, lock_type, &request);
+ if (r == DB_LOCK_NOTGRANTED) {
+ r = toku_db_wait_range_lock(db, txn, &request);
+ }
+
+ request.destroy();
+ return r;
+}
+
+// Setup and start an asynchronous lock request.
+int toku_db_start_range_lock(DB *db, DB_TXN *txn, const DBT *left_key, const DBT *right_key,
+ toku::lock_request::type lock_type, toku::lock_request *request) {
+ DB_TXN *txn_anc = txn_oldest_ancester(txn);
+ TXNID txn_anc_id = txn_anc->id64(txn_anc);
+ request->set(db->i->lt, txn_anc_id, left_key, right_key, lock_type, toku_is_big_txn(txn_anc));
+
+ const int r = request->start();
+ if (r == 0) {
+ db_txn_note_row_lock(db, txn_anc, left_key, right_key);
+ } else if (r == DB_LOCK_DEADLOCK) {
+ lock_timeout_callback callback = txn->mgrp->i->lock_wait_timeout_callback;
+ if (callback != nullptr) {
+ callback(db, txn_anc_id, left_key, right_key,
+ request->get_conflicting_txnid());
+ }
+ }
+ return r;
+}
+
+// Complete a lock request by waiting until the request is ready
+// and then storing the acquired lock if successful.
+int toku_db_wait_range_lock(DB *db, DB_TXN *txn, toku::lock_request *request) {
+ DB_TXN *txn_anc = txn_oldest_ancester(txn);
+ const DBT *left_key = request->get_left_key();
+ const DBT *right_key = request->get_right_key();
+ DB_ENV *env = db->dbenv;
+ uint64_t wait_time_msec = env->i->default_lock_timeout_msec;
+ if (env->i->get_lock_timeout_callback)
+ wait_time_msec = env->i->get_lock_timeout_callback(wait_time_msec);
+ uint64_t killed_time_msec = env->i->default_killed_time_msec;
+ if (env->i->get_killed_time_callback)
+ killed_time_msec = env->i->get_killed_time_callback(killed_time_msec);
+ const int r = request->wait(wait_time_msec, killed_time_msec, env->i->killed_callback);
+ if (r == 0) {
+ db_txn_note_row_lock(db, txn_anc, left_key, right_key);
+ } else if (r == DB_LOCK_NOTGRANTED) {
+ lock_timeout_callback callback = txn->mgrp->i->lock_wait_timeout_callback;
+ if (callback != nullptr) {
+ callback(db, txn_anc->id64(txn_anc), left_key, right_key,
+ request->get_conflicting_txnid());
+ }
+ }
+ return r;
+}
+
+int toku_db_get_point_write_lock(DB *db, DB_TXN *txn, const DBT *key) {
+ return toku_db_get_range_lock(db, txn, key, key, toku::lock_request::type::WRITE);
+}
+
+// acquire a point write lock on the key for a given txn.
+// this does not block the calling thread.
+void toku_db_grab_write_lock (DB *db, DBT *key, TOKUTXN tokutxn) {
+ DB_TXN *txn = toku_txn_get_container_db_txn(tokutxn);
+ DB_TXN *txn_anc = txn_oldest_ancester(txn);
+ TXNID txn_anc_id = txn_anc->id64(txn_anc);
+
+ // This lock request must succeed, so we do not want to wait
+ toku::lock_request request;
+ request.create();
+ request.set(db->i->lt, txn_anc_id, key, key, toku::lock_request::type::WRITE, toku_is_big_txn(txn_anc));
+ int r = request.start();
+ invariant_zero(r);
+ db_txn_note_row_lock(db, txn_anc, key, key);
+ request.destroy();
+}
+
+void toku_db_release_lt_key_ranges(DB_TXN *txn, txn_lt_key_ranges *ranges) {
+ toku::locktree *lt = ranges->lt;
+ TXNID txnid = txn->id64(txn);
+
+ // release all of the locks this txn has ever successfully
+ // acquired and stored in the range buffer for this locktree
+ lt->release_locks(txnid, ranges->buffer);
+ lt->get_manager()->note_mem_released(ranges->buffer->total_memory_size());
+ ranges->buffer->destroy();
+ toku_free(ranges->buffer);
+
+ // all of our locks have been released, so first try to wake up
+ // pending lock requests, then release our reference on the lt
+ toku::lock_request::retry_all_lock_requests(lt);
+
+ // Release our reference on this locktree
+ toku::locktree_manager *ltm = &txn->mgrp->i->ltm;
+ ltm->release_lt(lt);
+}
diff --git a/storage/tokudb/PerconaFT/src/ydb_row_lock.h b/storage/tokudb/PerconaFT/src/ydb_row_lock.h
new file mode 100644
index 00000000000..e5258a9a610
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_row_lock.h
@@ -0,0 +1,61 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+#include <ydb-internal.h>
+
+#include <locktree/lock_request.h>
+
+// Expose the escalate callback to ydb.cc,
+// so it can pass the function pointer to the locktree
+void toku_db_txn_escalate_callback(TXNID txnid, const toku::locktree *lt, const toku::range_buffer &buffer, void *extra);
+
+int toku_db_get_range_lock(DB *db, DB_TXN *txn, const DBT *left_key, const DBT *right_key,
+ toku::lock_request::type lock_type);
+
+int toku_db_start_range_lock(DB *db, DB_TXN *txn, const DBT *left_key, const DBT *right_key,
+ toku::lock_request::type lock_type, toku::lock_request *lock_request);
+
+int toku_db_wait_range_lock(DB *db, DB_TXN *txn, toku::lock_request *lock_request);
+
+int toku_db_get_point_write_lock(DB *db, DB_TXN *txn, const DBT *key);
+
+void toku_db_grab_write_lock(DB *db, DBT *key, TOKUTXN tokutxn);
+
+void toku_db_release_lt_key_ranges(DB_TXN *txn, txn_lt_key_ranges *ranges);
diff --git a/storage/tokudb/PerconaFT/src/ydb_txn.cc b/storage/tokudb/PerconaFT/src/ydb_txn.cc
new file mode 100644
index 00000000000..ae1f93011d1
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_txn.cc
@@ -0,0 +1,620 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <db.h>
+
+#include <portability/toku_race_tools.h>
+#include <portability/toku_atomic.h>
+
+#include <ft/cachetable/checkpoint.h>
+#include <ft/log_header.h>
+#include <ft/txn/txn_manager.h>
+
+
+#include "ydb-internal.h"
+#include "ydb_txn.h"
+#include "ydb_row_lock.h"
+
+static uint64_t toku_txn_id64(DB_TXN * txn) {
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ return toku_txn_get_root_id(db_txn_struct_i(txn)->tokutxn);
+}
+
+static void toku_txn_release_locks(DB_TXN *txn) {
+ // Prevent access to the locktree map while releasing.
+ // It is possible for lock escalation to attempt to
+ // modify this data structure while the txn commits.
+ toku_mutex_lock(&db_txn_struct_i(txn)->txn_mutex);
+
+ size_t num_ranges = db_txn_struct_i(txn)->lt_map.size();
+ for (size_t i = 0; i < num_ranges; i++) {
+ txn_lt_key_ranges ranges;
+ int r = db_txn_struct_i(txn)->lt_map.fetch(i, &ranges);
+ invariant_zero(r);
+ toku_db_release_lt_key_ranges(txn, &ranges);
+ }
+
+ toku_mutex_unlock(&db_txn_struct_i(txn)->txn_mutex);
+}
+
+static void toku_txn_destroy(DB_TXN *txn) {
+ db_txn_struct_i(txn)->lt_map.destroy();
+ toku_txn_destroy_txn(db_txn_struct_i(txn)->tokutxn);
+ toku_mutex_destroy(&db_txn_struct_i(txn)->txn_mutex);
+ toku_free(txn);
+}
+
+static int toku_txn_commit(DB_TXN * txn, uint32_t flags,
+ TXN_PROGRESS_POLL_FUNCTION poll, void *poll_extra,
+ bool release_mo_lock, bool low_priority) {
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ //Recursively kill off children
+ if (db_txn_struct_i(txn)->child) {
+ //commit of child sets the child pointer to NULL
+ int r_child = toku_txn_commit(db_txn_struct_i(txn)->child, flags, NULL, NULL, false, false);
+ if (r_child !=0 && !toku_env_is_panicked(txn->mgrp)) {
+ env_panic(txn->mgrp, r_child, "Recursive child commit failed during parent commit.\n");
+ }
+ //In a panicked env, the child may not be removed from the list.
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ }
+ assert(!db_txn_struct_i(txn)->child);
+ //Remove from parent
+ if (txn->parent) {
+ assert(db_txn_struct_i(txn->parent)->child == txn);
+ db_txn_struct_i(txn->parent)->child=NULL;
+ }
+ if (flags & DB_TXN_SYNC) {
+ toku_txn_force_fsync_on_commit(db_txn_struct_i(txn)->tokutxn);
+ flags &= ~DB_TXN_SYNC;
+ }
+ int nosync = (flags & DB_TXN_NOSYNC)!=0 || (db_txn_struct_i(txn)->flags&DB_TXN_NOSYNC);
+ flags &= ~DB_TXN_NOSYNC;
+
+ int r;
+ if (flags!=0) {
+ // frees the tokutxn
+ r = toku_txn_abort_txn(db_txn_struct_i(txn)->tokutxn, poll, poll_extra);
+ } else {
+ // frees the tokutxn
+ r = toku_txn_commit_txn(db_txn_struct_i(txn)->tokutxn, nosync,
+ poll, poll_extra);
+ }
+ if (r!=0 && !toku_env_is_panicked(txn->mgrp)) {
+ env_panic(txn->mgrp, r, "Error during commit.\n");
+ }
+ //If panicked, we're done.
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ assert_zero(r);
+
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ TOKULOGGER logger = txn->mgrp->i->logger;
+ LSN do_fsync_lsn;
+ bool do_fsync;
+ toku_txn_get_fsync_info(ttxn, &do_fsync, &do_fsync_lsn);
+ // remove the txn from the list of live transactions, and then
+ // release the lock tree locks. MVCC requires that toku_txn_complete_txn
+ // get called first, otherwise we have bugs, such as #4145 and #4153
+ toku_txn_complete_txn(ttxn);
+ toku_txn_release_locks(txn);
+ // this lock must be released after toku_txn_complete_txn and toku_txn_release_locks because
+ // this lock must be held until the references to the open FTs is released
+ // begin checkpoint logs these associations, so we must be protect
+ // the changing of these associations with checkpointing
+ if (release_mo_lock) {
+ if (low_priority) {
+ toku_low_priority_multi_operation_client_unlock();
+ } else {
+ toku_multi_operation_client_unlock();
+ }
+ }
+ toku_txn_maybe_fsync_log(logger, do_fsync_lsn, do_fsync);
+ if (flags!=0) {
+ r = EINVAL;
+ goto cleanup;
+ }
+cleanup:
+ toku_txn_destroy(txn);
+ return r;
+}
+
+static int toku_txn_abort(DB_TXN * txn,
+ TXN_PROGRESS_POLL_FUNCTION poll, void *poll_extra) {
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ //Recursively kill off children (abort or commit are both correct, commit is cheaper)
+ if (db_txn_struct_i(txn)->child) {
+ //commit of child sets the child pointer to NULL
+ int r_child = toku_txn_commit(db_txn_struct_i(txn)->child, DB_TXN_NOSYNC, NULL, NULL, false, false);
+ if (r_child !=0 && !toku_env_is_panicked(txn->mgrp)) {
+ env_panic(txn->mgrp, r_child, "Recursive child commit failed during parent abort.\n");
+ }
+ //In a panicked env, the child may not be removed from the list.
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ }
+ assert(!db_txn_struct_i(txn)->child);
+ //Remove from parent
+ if (txn->parent) {
+ assert(db_txn_struct_i(txn->parent)->child == txn);
+ db_txn_struct_i(txn->parent)->child=NULL;
+ }
+
+ int r = toku_txn_abort_txn(db_txn_struct_i(txn)->tokutxn, poll, poll_extra);
+ if (r!=0 && !toku_env_is_panicked(txn->mgrp)) {
+ env_panic(txn->mgrp, r, "Error during abort.\n");
+ }
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ assert_zero(r);
+ toku_txn_complete_txn(db_txn_struct_i(txn)->tokutxn);
+ toku_txn_release_locks(txn);
+ toku_txn_destroy(txn);
+ return r;
+}
+
+static int toku_txn_xa_prepare (DB_TXN *txn, TOKU_XA_XID *xid, uint32_t flags) {
+ int r = 0;
+ if (!txn) {
+ r = EINVAL;
+ goto exit;
+ }
+ if (txn->parent) {
+ r = 0; // make this a NO-OP, MySQL calls this
+ goto exit;
+ }
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ // Take the mo lock as soon as a non-readonly txn is found
+ bool holds_mo_lock;
+ holds_mo_lock = false;
+ if (!toku_txn_is_read_only(db_txn_struct_i(txn)->tokutxn)) {
+ // A readonly transaction does no logging, and therefore does not
+ // need the MO lock.
+ toku_multi_operation_client_lock();
+ holds_mo_lock = true;
+ }
+ //Recursively commit any children.
+ if (db_txn_struct_i(txn)->child) {
+ //commit of child sets the child pointer to NULL
+
+ // toku_txn_commit will take the mo_lock if not held and a non-readonly txn is found.
+ int r_child = toku_txn_commit(db_txn_struct_i(txn)->child, 0, NULL, NULL, false, false);
+ if (r_child !=0 && !toku_env_is_panicked(txn->mgrp)) {
+ env_panic(txn->mgrp, r_child, "Recursive child commit failed during parent commit.\n");
+ }
+ //In a panicked env, the child may not be removed from the list.
+ HANDLE_PANICKED_ENV(txn->mgrp);
+ }
+ assert(!db_txn_struct_i(txn)->child);
+ int nosync;
+ nosync = (flags & DB_TXN_NOSYNC)!=0 || (db_txn_struct_i(txn)->flags&DB_TXN_NOSYNC);
+ TOKUTXN ttxn;
+ ttxn = db_txn_struct_i(txn)->tokutxn;
+ toku_txn_prepare_txn(ttxn, xid, nosync);
+ TOKULOGGER logger;
+ logger = txn->mgrp->i->logger;
+ LSN do_fsync_lsn;
+ bool do_fsync;
+ toku_txn_get_fsync_info(ttxn, &do_fsync, &do_fsync_lsn);
+ // release the multi operation lock before fsyncing the log
+ if (holds_mo_lock) {
+ toku_multi_operation_client_unlock();
+ }
+ toku_txn_maybe_fsync_log(logger, do_fsync_lsn, do_fsync);
+exit:
+ return r;
+}
+
+// requires: must hold the multi operation lock. it is
+// released in toku_txn_xa_prepare before the fsync.
+static int toku_txn_prepare (DB_TXN *txn, uint8_t gid[DB_GID_SIZE], uint32_t flags) {
+ TOKU_XA_XID xid;
+ TOKU_ANNOTATE_NEW_MEMORY(&xid, sizeof(xid));
+ xid.formatID=0x756b6f54; // "Toku"
+ xid.gtrid_length=DB_GID_SIZE/2; // The maximum allowed gtrid length is 64. See the XA spec in source:/import/opengroup.org/C193.pdf page 20.
+ xid.bqual_length=DB_GID_SIZE/2; // The maximum allowed bqual length is 64.
+ memcpy(xid.data, gid, DB_GID_SIZE);
+ return toku_txn_xa_prepare(txn, &xid, flags);
+}
+
+static int toku_txn_txn_stat (DB_TXN *txn, struct txn_stat **txn_stat) {
+ XMALLOC(*txn_stat);
+ return toku_logger_txn_rollback_stats(db_txn_struct_i(txn)->tokutxn, *txn_stat);
+}
+
+static int locked_txn_txn_stat (DB_TXN *txn, struct txn_stat **txn_stat) {
+ int r = toku_txn_txn_stat(txn, txn_stat);
+ return r;
+}
+
+static int locked_txn_commit_with_progress(DB_TXN *txn, uint32_t flags,
+ TXN_PROGRESS_POLL_FUNCTION poll, void* poll_extra) {
+ bool holds_mo_lock = false;
+ bool low_priority = false;
+ TOKUTXN tokutxn = db_txn_struct_i(txn)->tokutxn;
+ if (!toku_txn_is_read_only(tokutxn)) {
+ // A readonly transaction does no logging, and therefore does not need the MO lock.
+ holds_mo_lock = true;
+ if (toku_is_big_tokutxn(tokutxn)) {
+ low_priority = true;
+ toku_low_priority_multi_operation_client_lock();
+ } else {
+ toku_multi_operation_client_lock();
+ }
+ }
+ // cannot begin a checkpoint.
+ // the multi operation lock is taken the first time we
+ // see a non-readonly txn in the recursive commit.
+ // But released in the first-level toku_txn_commit (if taken),
+ // this way, we don't hold it while we fsync the log.
+ int r = toku_txn_commit(txn, flags, poll, poll_extra, holds_mo_lock, low_priority);
+ return r;
+}
+
+static int locked_txn_abort_with_progress(DB_TXN *txn,
+ TXN_PROGRESS_POLL_FUNCTION poll, void* poll_extra) {
+ // cannot begin a checkpoint
+ // the multi operation lock is taken the first time we
+ // see a non-readonly txn in the abort (or recursive commit).
+ // But released here so we don't have to hold additional state.
+ bool holds_mo_lock = false;
+ bool low_priority = false;
+ TOKUTXN tokutxn = db_txn_struct_i(txn)->tokutxn;
+ if (!toku_txn_is_read_only(tokutxn)) {
+ // A readonly transaction does no logging, and therefore does not need the MO lock.
+ holds_mo_lock = true;
+ if (toku_is_big_tokutxn(tokutxn)) {
+ low_priority = true;
+ toku_low_priority_multi_operation_client_lock();
+ } else {
+ toku_multi_operation_client_lock();
+ }
+ }
+ int r = toku_txn_abort(txn, poll, poll_extra);
+ if (holds_mo_lock) {
+ if (low_priority) {
+ toku_low_priority_multi_operation_client_unlock();
+ } else {
+ toku_multi_operation_client_unlock();
+ }
+ }
+ return r;
+}
+
+int locked_txn_commit(DB_TXN *txn, uint32_t flags) {
+ int r = locked_txn_commit_with_progress(txn, flags, NULL, NULL);
+ return r;
+}
+
+int locked_txn_abort(DB_TXN *txn) {
+ int r = locked_txn_abort_with_progress(txn, NULL, NULL);
+ return r;
+}
+
+static void locked_txn_set_client_id(DB_TXN *txn, uint64_t client_id) {
+ toku_txn_set_client_id(db_txn_struct_i(txn)->tokutxn, client_id);
+}
+
+static uint64_t locked_txn_get_client_id(DB_TXN *txn) {
+ return toku_txn_get_client_id(db_txn_struct_i(txn)->tokutxn);
+}
+
+static int toku_txn_discard(DB_TXN *txn, uint32_t flags) {
+ // check parameters
+ if (flags != 0)
+ return EINVAL;
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ if (toku_txn_get_state(ttxn) != TOKUTXN_PREPARING)
+ return EINVAL;
+
+ bool low_priority;
+ if (toku_is_big_tokutxn(ttxn)) {
+ low_priority = true;
+ toku_low_priority_multi_operation_client_lock();
+ } else {
+ low_priority = false;
+ toku_multi_operation_client_lock();
+ }
+
+ // discard
+ toku_txn_discard_txn(ttxn);
+
+ // complete
+ toku_txn_complete_txn(ttxn);
+
+ // release locks
+ toku_txn_release_locks(txn);
+
+ if (low_priority) {
+ toku_low_priority_multi_operation_client_unlock();
+ } else {
+ toku_multi_operation_client_unlock();
+ }
+
+ // destroy
+ toku_txn_destroy(txn);
+
+ return 0;
+}
+
+static bool toku_txn_is_prepared(DB_TXN *txn) {
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ return toku_txn_get_state(ttxn) == TOKUTXN_PREPARING;
+}
+
+static DB_TXN *toku_txn_get_child(DB_TXN *txn) {
+ return db_txn_struct_i(txn)->child;
+}
+
+static uint64_t toku_txn_get_start_time(DB_TXN *txn) {
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ return toku_txn_get_start_time(ttxn);
+}
+
+static inline void txn_func_init(DB_TXN *txn) {
+#define STXN(name) txn->name = locked_txn_ ## name
+ STXN(abort);
+ STXN(commit);
+ STXN(abort_with_progress);
+ STXN(commit_with_progress);
+ STXN(txn_stat);
+ STXN(set_client_id);
+ STXN(get_client_id);
+#undef STXN
+#define SUTXN(name) txn->name = toku_txn_ ## name
+ SUTXN(prepare);
+ SUTXN(xa_prepare);
+ SUTXN(discard);
+#undef SUTXN
+ txn->id64 = toku_txn_id64;
+ txn->is_prepared = toku_txn_is_prepared;
+ txn->get_child = toku_txn_get_child;
+ txn->get_start_time = toku_txn_get_start_time;
+}
+
+//
+// Creates a transaction for the user
+// In our system, as far as the user is concerned, the rules are as follows:
+// - one cannot operate on a transaction if a child exists, with the exception of commit/abort
+// - one cannot operate on a transaction simultaneously in two separate threads
+// (the reason for this is that some operations may create a child transaction
+// as part of the function, such as env->dbremove and env->dbrename, and if
+// transactions could be operated on simulatenously in different threads, the first
+// rule above is violated)
+// - if a parent transaction is committed/aborted, the child transactions are recursively
+// committed
+//
+int toku_txn_begin(DB_ENV *env, DB_TXN * stxn, DB_TXN ** txn, uint32_t flags) {
+ HANDLE_PANICKED_ENV(env);
+ HANDLE_ILLEGAL_WORKING_PARENT_TXN(env, stxn); //Cannot create child while child already exists.
+ if (!toku_logger_is_open(env->i->logger))
+ return toku_ydb_do_error(env, EINVAL, "Environment does not have logging enabled\n");
+ if (!(env->i->open_flags & DB_INIT_TXN))
+ return toku_ydb_do_error(env, EINVAL, "Environment does not have transactions enabled\n");
+
+ uint32_t txn_flags = 0;
+ txn_flags |= DB_TXN_NOWAIT; //We do not support blocking locks. RFP remove this?
+
+ // handle whether txn is declared as read only
+ bool parent_txn_declared_read_only =
+ stxn &&
+ (db_txn_struct_i(stxn)->flags & DB_TXN_READ_ONLY);
+ bool txn_declared_read_only = false;
+ if (flags & DB_TXN_READ_ONLY) {
+ txn_declared_read_only = true;
+ txn_flags |= DB_TXN_READ_ONLY;
+ flags &= ~(DB_TXN_READ_ONLY);
+ }
+ if (txn_declared_read_only && stxn &&
+ !parent_txn_declared_read_only
+ )
+ {
+ return toku_ydb_do_error(
+ env,
+ EINVAL,
+ "Current transaction set as read only, but parent transaction is not\n"
+ );
+ }
+ if (parent_txn_declared_read_only)
+ {
+ // don't require child transaction to also set transaction as read only
+ // if parent has already done so
+ txn_flags |= DB_TXN_READ_ONLY;
+ txn_declared_read_only = true;
+ }
+
+
+ TOKU_ISOLATION child_isolation = TOKU_ISO_SERIALIZABLE;
+ uint32_t iso_flags = flags & DB_ISOLATION_FLAGS;
+ if (!(iso_flags == 0 ||
+ iso_flags == DB_TXN_SNAPSHOT ||
+ iso_flags == DB_READ_COMMITTED ||
+ iso_flags == DB_READ_COMMITTED_ALWAYS ||
+ iso_flags == DB_READ_UNCOMMITTED ||
+ iso_flags == DB_SERIALIZABLE ||
+ iso_flags == DB_INHERIT_ISOLATION)
+ )
+ {
+ return toku_ydb_do_error(
+ env,
+ EINVAL,
+ "Invalid isolation flags set\n"
+ );
+ }
+ flags &= ~iso_flags;
+
+ switch (iso_flags) {
+ case (DB_INHERIT_ISOLATION):
+ if (stxn) {
+ child_isolation = db_txn_struct_i(stxn)->iso;
+ }
+ else {
+ return toku_ydb_do_error(
+ env,
+ EINVAL,
+ "Cannot set DB_INHERIT_ISOLATION when no parent exists\n"
+ );
+ }
+ break;
+ case (DB_READ_COMMITTED):
+ child_isolation = TOKU_ISO_READ_COMMITTED;
+ break;
+ case (DB_READ_COMMITTED_ALWAYS):
+ child_isolation = TOKU_ISO_READ_COMMITTED_ALWAYS;
+ break;
+ case (DB_READ_UNCOMMITTED):
+ child_isolation = TOKU_ISO_READ_UNCOMMITTED;
+ break;
+ case (DB_TXN_SNAPSHOT):
+ child_isolation = TOKU_ISO_SNAPSHOT;
+ break;
+ case (DB_SERIALIZABLE):
+ child_isolation = TOKU_ISO_SERIALIZABLE;
+ break;
+ case (0):
+ child_isolation = stxn ? db_txn_struct_i(stxn)->iso : TOKU_ISO_SERIALIZABLE;
+ break;
+ default:
+ assert(false); // error path is above, so this should not happen
+ break;
+ }
+ if (stxn && child_isolation != db_txn_struct_i(stxn)->iso) {
+ return toku_ydb_do_error(
+ env,
+ EINVAL,
+ "Cannot set isolation level of transaction to something different \
+ isolation level\n"
+ );
+ }
+
+ if (flags&DB_TXN_NOWAIT) {
+ txn_flags |= DB_TXN_NOWAIT;
+ flags &= ~DB_TXN_NOWAIT;
+ }
+ if (flags&DB_TXN_NOSYNC) {
+ txn_flags |= DB_TXN_NOSYNC;
+ flags &= ~DB_TXN_NOSYNC;
+ }
+ if (flags!=0) return toku_ydb_do_error(env, EINVAL, "Invalid flags passed to DB_ENV->txn_begin\n");
+
+ struct __toku_db_txn_external *XCALLOC(eresult); // so the internal stuff is stuck on the end.
+ DB_TXN *result = &eresult->external_part;
+
+ result->mgrp = env;
+ txn_func_init(result);
+
+ result->parent = stxn;
+ db_txn_struct_i(result)->flags = txn_flags;
+ db_txn_struct_i(result)->iso = child_isolation;
+ db_txn_struct_i(result)->lt_map.create_no_array();
+
+ toku_mutex_init(&db_txn_struct_i(result)->txn_mutex, NULL);
+
+ TXN_SNAPSHOT_TYPE snapshot_type;
+ switch(db_txn_struct_i(result)->iso){
+ case(TOKU_ISO_SNAPSHOT):
+ {
+ snapshot_type = TXN_SNAPSHOT_ROOT;
+ break;
+ }
+ case(TOKU_ISO_READ_COMMITTED):
+ {
+ snapshot_type = TXN_SNAPSHOT_CHILD;
+ break;
+ }
+ case(TOKU_ISO_READ_COMMITTED_ALWAYS) :
+ {
+ snapshot_type = TXN_COPIES_SNAPSHOT;
+ break;
+ }
+ default:
+ {
+ snapshot_type = TXN_SNAPSHOT_NONE;
+ break;
+ }
+ }
+ int r = toku_txn_begin_with_xid(
+ stxn ? db_txn_struct_i(stxn)->tokutxn : 0,
+ &db_txn_struct_i(result)->tokutxn,
+ env->i->logger,
+ TXNID_PAIR_NONE,
+ snapshot_type,
+ result,
+ false, // for_recovery
+ txn_declared_read_only // read_only
+ );
+ if (r != 0) {
+ toku_free(result);
+ return r;
+ }
+
+ //Add to the list of children for the parent.
+ if (result->parent) {
+ assert(!db_txn_struct_i(result->parent)->child);
+ db_txn_struct_i(result->parent)->child = result;
+ }
+
+ *txn = result;
+ return 0;
+}
+
+void toku_keep_prepared_txn_callback (DB_ENV *env, TOKUTXN tokutxn) {
+ struct __toku_db_txn_external *XCALLOC(eresult);
+ DB_TXN *result = &eresult->external_part;
+ result->mgrp = env;
+ txn_func_init(result);
+
+ result->parent = NULL;
+
+ db_txn_struct_i(result)->tokutxn = tokutxn;
+ db_txn_struct_i(result)->lt_map.create();
+
+ toku_txn_set_container_db_txn(tokutxn, result);
+
+ toku_mutex_init(&db_txn_struct_i(result)->txn_mutex, NULL);
+}
+
+// Test-only function
+void toku_increase_last_xid(DB_ENV *env, uint64_t increment) {
+ toku_txn_manager_increase_last_xid(toku_logger_get_txn_manager(env->i->logger), increment);
+}
+
+bool toku_is_big_txn(DB_TXN *txn) {
+ return toku_is_big_tokutxn(db_txn_struct_i(txn)->tokutxn);
+}
+
+bool toku_is_big_tokutxn(TOKUTXN tokutxn) {
+ return toku_txn_has_spilled_rollback(tokutxn);
+}
diff --git a/storage/tokudb/PerconaFT/src/ydb_txn.h b/storage/tokudb/PerconaFT/src/ydb_txn.h
new file mode 100644
index 00000000000..6f4fabf3dbc
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_txn.h
@@ -0,0 +1,59 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+// begin, commit, and abort use the multi operation lock
+// internally to synchronize with begin checkpoint. callers
+// should not hold the multi operation lock.
+
+int toku_txn_begin(DB_ENV *env, DB_TXN * stxn, DB_TXN ** txn, uint32_t flags);
+
+void toku_txn_note_db_row_lock(DB_TXN *txn, DB *db, const DBT *left_key, const DBT *right_key);
+
+int locked_txn_commit(DB_TXN *txn, uint32_t flags);
+
+int locked_txn_abort(DB_TXN *txn);
+
+void toku_keep_prepared_txn_callback(DB_ENV *env, TOKUTXN tokutxn);
+
+bool toku_is_big_txn(DB_TXN *txn);
+bool toku_is_big_tokutxn(TOKUTXN tokutxn);
+
+// Test-only function
+extern "C" void toku_increase_last_xid(DB_ENV *env, uint64_t increment) __attribute__((__visibility__("default")));
diff --git a/storage/tokudb/PerconaFT/src/ydb_write.cc b/storage/tokudb/PerconaFT/src/ydb_write.cc
new file mode 100644
index 00000000000..8cd7e22019b
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_write.cc
@@ -0,0 +1,1136 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#include <db.h>
+#include "ydb-internal.h"
+#include "indexer.h"
+#include <ft/log_header.h>
+#include <ft/cachetable/checkpoint.h>
+#include "ydb_row_lock.h"
+#include "ydb_write.h"
+#include "ydb_db.h"
+#include <portability/toku_atomic.h>
+#include <util/status.h>
+
+static YDB_WRITE_LAYER_STATUS_S ydb_write_layer_status;
+#ifdef STATUS_VALUE
+#undef STATUS_VALUE
+#endif
+#define STATUS_VALUE(x) ydb_write_layer_status.status[x].value.num
+
+#define STATUS_INIT(k,c,t,l,inc) TOKUFT_STATUS_INIT(ydb_write_layer_status, k, c, t, l, inc)
+
+static void
+ydb_write_layer_status_init (void) {
+ // Note, this function initializes the keyname, type, and legend fields.
+ // Value fields are initialized to zero by compiler.
+ STATUS_INIT(YDB_LAYER_NUM_INSERTS, nullptr, UINT64, "dictionary inserts", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_INSERTS_FAIL, nullptr, UINT64, "dictionary inserts fail", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_DELETES, nullptr, UINT64, "dictionary deletes", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_DELETES_FAIL, nullptr, UINT64, "dictionary deletes fail", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_UPDATES, nullptr, UINT64, "dictionary updates", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_UPDATES_FAIL, nullptr, UINT64, "dictionary updates fail", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_UPDATES_BROADCAST, nullptr, UINT64, "dictionary broadcast updates", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_UPDATES_BROADCAST_FAIL, nullptr, UINT64, "dictionary broadcast updates fail", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_MULTI_INSERTS, nullptr, UINT64, "dictionary multi inserts", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_MULTI_INSERTS_FAIL, nullptr, UINT64, "dictionary multi inserts fail", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_MULTI_DELETES, nullptr, UINT64, "dictionary multi deletes", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_MULTI_DELETES_FAIL, nullptr, UINT64, "dictionary multi deletes fail", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_MULTI_UPDATES, nullptr, UINT64, "dictionary updates multi", TOKU_ENGINE_STATUS);
+ STATUS_INIT(YDB_LAYER_NUM_MULTI_UPDATES_FAIL, nullptr, UINT64, "dictionary updates multi fail", TOKU_ENGINE_STATUS);
+ ydb_write_layer_status.initialized = true;
+}
+#undef STATUS_INIT
+
+void
+ydb_write_layer_get_status(YDB_WRITE_LAYER_STATUS statp) {
+ if (!ydb_write_layer_status.initialized)
+ ydb_write_layer_status_init();
+ *statp = ydb_write_layer_status;
+}
+
+
+static inline uint32_t
+get_prelocked_flags(uint32_t flags) {
+ uint32_t lock_flags = flags & (DB_PRELOCKED | DB_PRELOCKED_WRITE);
+ return lock_flags;
+}
+
+// these next two static functions are defined
+// both here and ydb.c. We should find a good
+// place for them.
+static int
+ydb_getf_do_nothing(DBT const* UU(key), DBT const* UU(val), void* UU(extra)) {
+ return 0;
+}
+
+// Check if the available file system space is less than the reserve
+// Returns ENOSPC if not enough space, othersize 0
+static inline int
+env_check_avail_fs_space(DB_ENV *env) {
+ int r = env->i->fs_state == FS_RED ? ENOSPC : 0;
+ if (r) {
+ env->i->enospc_redzone_ctr++;
+ }
+ return r;
+}
+
+// Return 0 if proposed pair do not violate size constraints of DB
+// (insertion is legal)
+// Return non zero otherwise.
+static int
+db_put_check_size_constraints(DB *db, const DBT *key, const DBT *val) {
+ int r = 0;
+ unsigned int klimit, vlimit;
+
+ toku_ft_get_maximum_advised_key_value_lengths(&klimit, &vlimit);
+ if (key->size > klimit) {
+ r = toku_ydb_do_error(db->dbenv, EINVAL,
+ "The largest key allowed is %u bytes", klimit);
+ } else if (val->size > vlimit) {
+ r = toku_ydb_do_error(db->dbenv, EINVAL,
+ "The largest value allowed is %u bytes", vlimit);
+ }
+ return r;
+}
+
+//Return 0 if insert is legal
+static int
+db_put_check_overwrite_constraint(DB *db, DB_TXN *txn, DBT *key,
+ uint32_t lock_flags, uint32_t overwrite_flag) {
+ int r;
+
+ if (overwrite_flag == 0) { // 0 (yesoverwrite) does not impose constraints.
+ r = 0;
+ } else if (overwrite_flag == DB_NOOVERWRITE) {
+ // Check if (key,anything) exists in dictionary.
+ // If exists, fail. Otherwise, do insert.
+ // The DB_RMW flag causes the cursor to grab a write lock instead of a read lock on the key if it exists.
+ r = db_getf_set(db, txn, lock_flags|DB_SERIALIZABLE|DB_RMW, key, ydb_getf_do_nothing, NULL);
+ if (r == DB_NOTFOUND)
+ r = 0;
+ else if (r == 0)
+ r = DB_KEYEXIST;
+ //Any other error is passed through.
+ } else if (overwrite_flag == DB_NOOVERWRITE_NO_ERROR) {
+ r = 0;
+ } else {
+ //Other flags are not (yet) supported.
+ r = EINVAL;
+ }
+ return r;
+}
+
+
+int
+toku_db_del(DB *db, DB_TXN *txn, DBT *key, uint32_t flags, bool holds_mo_lock) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ HANDLE_READ_ONLY_TXN(txn);
+
+ uint32_t unchecked_flags = flags;
+ //DB_DELETE_ANY means delete regardless of whether it exists in the db.
+ bool error_if_missing = (bool)(!(flags&DB_DELETE_ANY));
+ unchecked_flags &= ~DB_DELETE_ANY;
+ uint32_t lock_flags = get_prelocked_flags(flags);
+ unchecked_flags &= ~lock_flags;
+ bool do_locking = (bool)(db->i->lt && !(lock_flags&DB_PRELOCKED_WRITE));
+
+ int r = 0;
+ if (unchecked_flags!=0) {
+ r = EINVAL;
+ }
+
+ if (r == 0 && error_if_missing) {
+ //Check if the key exists in the db.
+ r = db_getf_set(db, txn, lock_flags|DB_SERIALIZABLE|DB_RMW, key, ydb_getf_do_nothing, NULL);
+ }
+ if (r == 0 && do_locking) {
+ //Do locking if necessary.
+ r = toku_db_get_point_write_lock(db, txn, key);
+ }
+ if (r == 0) {
+ //Do the actual deleting.
+ if (!holds_mo_lock) toku_multi_operation_client_lock();
+ toku_ft_delete(db->i->ft_handle, key, txn ? db_txn_struct_i(txn)->tokutxn : 0);
+ if (!holds_mo_lock) toku_multi_operation_client_unlock();
+ }
+
+ if (r == 0) {
+ STATUS_VALUE(YDB_LAYER_NUM_DELETES)++; // accountability
+ }
+ else {
+ STATUS_VALUE(YDB_LAYER_NUM_DELETES_FAIL)++; // accountability
+ }
+ return r;
+}
+
+static int
+db_put(DB *db, DB_TXN *txn, DBT *key, DBT *val, int flags, bool do_log) {
+ int r = 0;
+ bool unique = false;
+ enum ft_msg_type type = FT_INSERT;
+ if (flags == DB_NOOVERWRITE) {
+ unique = true;
+ } else if (flags == DB_NOOVERWRITE_NO_ERROR) {
+ type = FT_INSERT_NO_OVERWRITE;
+ } else if (flags != 0) {
+ // All other non-zero flags are unsupported
+ r = EINVAL;
+ }
+ if (r == 0) {
+ TOKUTXN ttxn = txn ? db_txn_struct_i(txn)->tokutxn : nullptr;
+ if (unique) {
+ r = toku_ft_insert_unique(db->i->ft_handle, key, val, ttxn, do_log);
+ } else {
+ toku_ft_maybe_insert(db->i->ft_handle, key, val, ttxn, false, ZERO_LSN, do_log, type);
+ }
+ invariant(r == DB_KEYEXIST || r == 0);
+ }
+ return r;
+}
+
+int
+toku_db_put(DB *db, DB_TXN *txn, DBT *key, DBT *val, uint32_t flags, bool holds_mo_lock) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ HANDLE_READ_ONLY_TXN(txn);
+ int r = 0;
+
+ uint32_t lock_flags = get_prelocked_flags(flags);
+ flags &= ~lock_flags;
+
+ r = db_put_check_size_constraints(db, key, val);
+
+ //Do locking if necessary.
+ bool do_locking = (bool)(db->i->lt && !(lock_flags&DB_PRELOCKED_WRITE));
+ if (r == 0 && do_locking) {
+ r = toku_db_get_point_write_lock(db, txn, key);
+ }
+ if (r == 0) {
+ //Insert into the ft.
+ if (!holds_mo_lock) toku_multi_operation_client_lock();
+ r = db_put(db, txn, key, val, flags, true);
+ if (!holds_mo_lock) toku_multi_operation_client_unlock();
+ }
+
+ if (r == 0) {
+ // helgrind flags a race on this status update. we increment it atomically to satisfy helgrind.
+ // STATUS_VALUE(YDB_LAYER_NUM_INSERTS)++; // accountability
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(YDB_LAYER_NUM_INSERTS), 1);
+ } else {
+ // STATUS_VALUE(YDB_LAYER_NUM_INSERTS_FAIL)++; // accountability
+ (void) toku_sync_fetch_and_add(&STATUS_VALUE(YDB_LAYER_NUM_INSERTS_FAIL), 1);
+ }
+
+ return r;
+}
+
+static int
+toku_db_update(DB *db, DB_TXN *txn,
+ const DBT *key,
+ const DBT *update_function_extra,
+ uint32_t flags) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ HANDLE_READ_ONLY_TXN(txn);
+ int r = 0;
+
+ uint32_t lock_flags = get_prelocked_flags(flags);
+ flags &= ~lock_flags;
+
+ r = db_put_check_size_constraints(db, key, update_function_extra);
+ if (r != 0) { goto cleanup; }
+
+ bool do_locking;
+ do_locking = (db->i->lt && !(lock_flags & DB_PRELOCKED_WRITE));
+ if (do_locking) {
+ r = toku_db_get_point_write_lock(db, txn, key);
+ if (r != 0) { goto cleanup; }
+ }
+
+ TOKUTXN ttxn;
+ ttxn = txn ? db_txn_struct_i(txn)->tokutxn : NULL;
+ toku_multi_operation_client_lock();
+ toku_ft_maybe_update(db->i->ft_handle, key, update_function_extra, ttxn,
+ false, ZERO_LSN, true);
+ toku_multi_operation_client_unlock();
+
+cleanup:
+ if (r == 0)
+ STATUS_VALUE(YDB_LAYER_NUM_UPDATES)++; // accountability
+ else
+ STATUS_VALUE(YDB_LAYER_NUM_UPDATES_FAIL)++; // accountability
+ return r;
+}
+
+
+// DB_IS_RESETTING_OP is true if the dictionary should be considered as if created by this transaction.
+// For example, it will be true if toku_db_update_broadcast() is used to implement a schema change (such
+// as adding a column), and will be false if used simply to update all the rows of a table (such as
+// incrementing a field).
+static int
+toku_db_update_broadcast(DB *db, DB_TXN *txn,
+ const DBT *update_function_extra,
+ uint32_t flags) {
+ HANDLE_PANICKED_DB(db);
+ HANDLE_DB_ILLEGAL_WORKING_PARENT_TXN(db, txn);
+ HANDLE_READ_ONLY_TXN(txn);
+ int r = 0;
+
+ uint32_t lock_flags = get_prelocked_flags(flags);
+ flags &= ~lock_flags;
+ uint32_t is_resetting_op_flag = flags & DB_IS_RESETTING_OP;
+ flags &= is_resetting_op_flag;
+ bool is_resetting_op = (is_resetting_op_flag != 0);
+
+
+ if (is_resetting_op) {
+ if (txn->parent != NULL) {
+ r = EINVAL; // cannot have a parent if you are a resetting op
+ goto cleanup;
+ }
+ r = toku_db_pre_acquire_fileops_lock(db, txn);
+ if (r != 0) { goto cleanup; }
+ }
+ {
+ DBT null_key;
+ toku_init_dbt(&null_key);
+ r = db_put_check_size_constraints(db, &null_key, update_function_extra);
+ if (r != 0) { goto cleanup; }
+ }
+
+ bool do_locking;
+ do_locking = (db->i->lt && !(lock_flags & DB_PRELOCKED_WRITE));
+ if (do_locking) {
+ r = toku_db_pre_acquire_table_lock(db, txn);
+ if (r != 0) { goto cleanup; }
+ }
+
+ TOKUTXN ttxn;
+ ttxn = txn ? db_txn_struct_i(txn)->tokutxn : NULL;
+ toku_multi_operation_client_lock();
+ toku_ft_maybe_update_broadcast(db->i->ft_handle, update_function_extra, ttxn,
+ false, ZERO_LSN, true, is_resetting_op);
+ toku_multi_operation_client_unlock();
+
+cleanup:
+ if (r == 0)
+ STATUS_VALUE(YDB_LAYER_NUM_UPDATES_BROADCAST)++; // accountability
+ else
+ STATUS_VALUE(YDB_LAYER_NUM_UPDATES_BROADCAST_FAIL)++; // accountability
+ return r;
+}
+
+static void
+log_del_single(DB_TXN *txn, FT_HANDLE ft_handle, const DBT *key) {
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ toku_ft_log_del(ttxn, ft_handle, key);
+}
+
+static uint32_t
+sum_size(uint32_t num_arrays, DBT_ARRAY keys[], uint32_t overhead) {
+ uint32_t sum = 0;
+ for (uint32_t i = 0; i < num_arrays; i++) {
+ for (uint32_t j = 0; j < keys[i].size; j++) {
+ sum += keys[i].dbts[j].size + overhead;
+ }
+ }
+ return sum;
+}
+
+static void
+log_del_multiple(DB_TXN *txn, DB *src_db, const DBT *key, const DBT *val, uint32_t num_dbs, FT_HANDLE fts[], DBT_ARRAY keys[]) {
+ if (num_dbs > 0) {
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ FT_HANDLE src_ft = src_db ? src_db->i->ft_handle : NULL;
+ uint32_t del_multiple_size = key->size + val->size + num_dbs*sizeof (uint32_t) + toku_log_enq_delete_multiple_overhead;
+ uint32_t del_single_sizes = sum_size(num_dbs, keys, toku_log_enq_delete_any_overhead);
+ if (del_single_sizes < del_multiple_size) {
+ for (uint32_t i = 0; i < num_dbs; i++) {
+ for (uint32_t j = 0; j < keys[i].size; j++) {
+ log_del_single(txn, fts[i], &keys[i].dbts[j]);
+ }
+ }
+ } else {
+ toku_ft_log_del_multiple(ttxn, src_ft, fts, num_dbs, key, val);
+ }
+ }
+}
+
+static uint32_t
+lookup_src_db(uint32_t num_dbs, DB *db_array[], DB *src_db) {
+ uint32_t which_db;
+ for (which_db = 0; which_db < num_dbs; which_db++)
+ if (db_array[which_db] == src_db)
+ break;
+ return which_db;
+}
+
+static int
+do_del_multiple(DB_TXN *txn, uint32_t num_dbs, DB *db_array[], DBT_ARRAY keys[], DB *src_db, const DBT *src_key, bool indexer_shortcut) {
+ int r = 0;
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ for (uint32_t which_db = 0; r == 0 && which_db < num_dbs; which_db++) {
+ DB *db = db_array[which_db];
+
+ paranoid_invariant(keys[which_db].size <= keys[which_db].capacity);
+
+ // if db is being indexed by an indexer, then insert a delete message into the db if the src key is to the left or equal to the
+ // indexers cursor. we have to get the src_db from the indexer and find it in the db_array.
+ int do_delete = true;
+ DB_INDEXER *indexer = toku_db_get_indexer(db);
+ if (indexer && !indexer_shortcut) { // if this db is the index under construction
+ DB *indexer_src_db = toku_indexer_get_src_db(indexer);
+ invariant(indexer_src_db != NULL);
+ const DBT *indexer_src_key;
+ if (src_db == indexer_src_db)
+ indexer_src_key = src_key;
+ else {
+ uint32_t which_src_db = lookup_src_db(num_dbs, db_array, indexer_src_db);
+ invariant(which_src_db < num_dbs);
+ // The indexer src db must have exactly one item or we don't know how to continue.
+ invariant(keys[which_src_db].size == 1);
+ indexer_src_key = &keys[which_src_db].dbts[0];
+ }
+ do_delete = toku_indexer_should_insert_key(indexer, indexer_src_key);
+ toku_indexer_update_estimate(indexer);
+ }
+ if (do_delete) {
+ for (uint32_t i = 0; i < keys[which_db].size; i++) {
+ toku_ft_maybe_delete(db->i->ft_handle, &keys[which_db].dbts[i], ttxn, false, ZERO_LSN, false);
+ }
+ }
+ }
+ return r;
+}
+
+//
+// if a hot index is in progress, gets the indexer
+// also verifies that there is at most one hot index
+// in progress. If it finds more than one, then returns EINVAL
+//
+static int
+get_indexer_if_exists(
+ uint32_t num_dbs,
+ DB **db_array,
+ DB *src_db,
+ DB_INDEXER** indexerp,
+ bool *src_db_is_indexer_src
+ )
+{
+ int r = 0;
+ DB_INDEXER* first_indexer = NULL;
+ for (uint32_t i = 0; i < num_dbs; i++) {
+ DB_INDEXER* indexer = toku_db_get_indexer(db_array[i]);
+ if (indexer) {
+ if (!first_indexer) {
+ first_indexer = indexer;
+ }
+ else if (first_indexer != indexer) {
+ r = EINVAL;
+ }
+ }
+ }
+ if (r == 0) {
+ if (first_indexer) {
+ DB* indexer_src_db = toku_indexer_get_src_db(first_indexer);
+ // we should just make this an invariant
+ if (src_db == indexer_src_db) {
+ *src_db_is_indexer_src = true;
+ }
+ }
+ *indexerp = first_indexer;
+ }
+ return r;
+}
+
+int
+env_del_multiple(
+ DB_ENV *env,
+ DB *src_db,
+ DB_TXN *txn,
+ const DBT *src_key,
+ const DBT *src_val,
+ uint32_t num_dbs,
+ DB **db_array,
+ DBT_ARRAY *keys,
+ uint32_t *flags_array)
+{
+ int r;
+ DBT_ARRAY del_keys[num_dbs];
+ DB_INDEXER* indexer = NULL;
+
+ HANDLE_PANICKED_ENV(env);
+ HANDLE_READ_ONLY_TXN(txn);
+
+ uint32_t lock_flags[num_dbs];
+ uint32_t remaining_flags[num_dbs];
+ FT_HANDLE fts[num_dbs];
+ bool indexer_lock_taken = false;
+ bool src_same = false;
+ bool indexer_shortcut = false;
+ if (!txn) {
+ r = EINVAL;
+ goto cleanup;
+ }
+ if (!env->i->generate_row_for_del) {
+ r = EINVAL;
+ goto cleanup;
+ }
+
+ HANDLE_ILLEGAL_WORKING_PARENT_TXN(env, txn);
+ r = get_indexer_if_exists(num_dbs, db_array, src_db, &indexer, &src_same);
+ if (r) {
+ goto cleanup;
+ }
+
+ for (uint32_t which_db = 0; which_db < num_dbs; which_db++) {
+ DB *db = db_array[which_db];
+ lock_flags[which_db] = get_prelocked_flags(flags_array[which_db]);
+ remaining_flags[which_db] = flags_array[which_db] & ~lock_flags[which_db];
+
+ if (db == src_db) {
+ del_keys[which_db].size = 1;
+ del_keys[which_db].capacity = 1;
+ del_keys[which_db].dbts = const_cast<DBT*>(src_key);
+ }
+ else {
+ //Generate the key
+ r = env->i->generate_row_for_del(db, src_db, &keys[which_db], src_key, src_val);
+ if (r != 0) goto cleanup;
+ del_keys[which_db] = keys[which_db];
+ paranoid_invariant(del_keys[which_db].size <= del_keys[which_db].capacity);
+ }
+
+ if (remaining_flags[which_db] & ~DB_DELETE_ANY) {
+ r = EINVAL;
+ goto cleanup;
+ }
+ bool error_if_missing = (bool)(!(remaining_flags[which_db]&DB_DELETE_ANY));
+ for (uint32_t which_key = 0; which_key < del_keys[which_db].size; which_key++) {
+ DBT *del_key = &del_keys[which_db].dbts[which_key];
+ if (error_if_missing) {
+ //Check if the key exists in the db.
+ //Grabs a write lock
+ r = db_getf_set(db, txn, lock_flags[which_db]|DB_SERIALIZABLE|DB_RMW, del_key, ydb_getf_do_nothing, NULL);
+ if (r != 0) goto cleanup;
+ } else if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) { //Do locking if necessary.
+ //Needs locking
+ r = toku_db_get_point_write_lock(db, txn, del_key);
+ if (r != 0) goto cleanup;
+ }
+ }
+ fts[which_db] = db->i->ft_handle;
+ }
+
+ if (indexer) {
+ // do a cheap check
+ if (src_same) {
+ bool may_insert = toku_indexer_may_insert(indexer, src_key);
+ if (!may_insert) {
+ toku_indexer_lock(indexer);
+ indexer_lock_taken = true;
+ }
+ else {
+ indexer_shortcut = true;
+ }
+ }
+ }
+ toku_multi_operation_client_lock();
+ log_del_multiple(txn, src_db, src_key, src_val, num_dbs, fts, del_keys);
+ r = do_del_multiple(txn, num_dbs, db_array, del_keys, src_db, src_key, indexer_shortcut);
+ toku_multi_operation_client_unlock();
+ if (indexer_lock_taken) {
+ toku_indexer_unlock(indexer);
+ }
+
+cleanup:
+ if (r == 0)
+ STATUS_VALUE(YDB_LAYER_NUM_MULTI_DELETES) += num_dbs; // accountability
+ else
+ STATUS_VALUE(YDB_LAYER_NUM_MULTI_DELETES_FAIL) += num_dbs; // accountability
+ return r;
+}
+
+static void
+log_put_multiple(DB_TXN *txn, DB *src_db, const DBT *src_key, const DBT *src_val, uint32_t num_dbs, FT_HANDLE fts[]) {
+ if (num_dbs > 0) {
+ TOKUTXN ttxn = db_txn_struct_i(txn)->tokutxn;
+ FT_HANDLE src_ft = src_db ? src_db->i->ft_handle : NULL;
+ toku_ft_log_put_multiple(ttxn, src_ft, fts, num_dbs, src_key, src_val);
+ }
+}
+
+// Requires: If remaining_flags is non-null, this function performs any required uniqueness checks
+// Otherwise, the caller is responsible.
+static int
+do_put_multiple(DB_TXN *txn, uint32_t num_dbs, DB *db_array[], DBT_ARRAY keys[], DBT_ARRAY vals[], uint32_t *remaining_flags, DB *src_db, const DBT *src_key, bool indexer_shortcut) {
+ int r = 0;
+ for (uint32_t which_db = 0; which_db < num_dbs; which_db++) {
+ DB *db = db_array[which_db];
+
+ invariant(keys[which_db].size == vals[which_db].size);
+ paranoid_invariant(keys[which_db].size <= keys[which_db].capacity);
+ paranoid_invariant(vals[which_db].size <= vals[which_db].capacity);
+
+ if (keys[which_db].size > 0) {
+ bool do_put = true;
+ DB_INDEXER *indexer = toku_db_get_indexer(db);
+ if (indexer && !indexer_shortcut) { // if this db is the index under construction
+ DB *indexer_src_db = toku_indexer_get_src_db(indexer);
+ invariant(indexer_src_db != NULL);
+ const DBT *indexer_src_key;
+ if (src_db == indexer_src_db)
+ indexer_src_key = src_key;
+ else {
+ uint32_t which_src_db = lookup_src_db(num_dbs, db_array, indexer_src_db);
+ invariant(which_src_db < num_dbs);
+ // The indexer src db must have exactly one item or we don't know how to continue.
+ invariant(keys[which_src_db].size == 1);
+ indexer_src_key = &keys[which_src_db].dbts[0];
+ }
+ do_put = toku_indexer_should_insert_key(indexer, indexer_src_key);
+ toku_indexer_update_estimate(indexer);
+ }
+ if (do_put) {
+ for (uint32_t i = 0; i < keys[which_db].size; i++) {
+ int flags = 0;
+ if (remaining_flags != nullptr) {
+ flags = remaining_flags[which_db];
+ invariant(!(flags & DB_NOOVERWRITE_NO_ERROR));
+ }
+ r = db_put(db, txn, &keys[which_db].dbts[i], &vals[which_db].dbts[i], flags, false);
+ if (r != 0) {
+ goto done;
+ }
+ }
+ }
+ }
+ }
+done:
+ return r;
+}
+
+static int
+env_put_multiple_internal(
+ DB_ENV *env,
+ DB *src_db,
+ DB_TXN *txn,
+ const DBT *src_key,
+ const DBT *src_val,
+ uint32_t num_dbs,
+ DB **db_array,
+ DBT_ARRAY *keys,
+ DBT_ARRAY *vals,
+ uint32_t *flags_array)
+{
+ int r;
+ DBT_ARRAY put_keys[num_dbs];
+ DBT_ARRAY put_vals[num_dbs];
+ DB_INDEXER* indexer = NULL;
+
+ HANDLE_PANICKED_ENV(env);
+ HANDLE_READ_ONLY_TXN(txn);
+
+ uint32_t lock_flags[num_dbs];
+ uint32_t remaining_flags[num_dbs];
+ FT_HANDLE fts[num_dbs];
+ bool indexer_shortcut = false;
+ bool indexer_lock_taken = false;
+ bool src_same = false;
+
+ if (!txn || !num_dbs) {
+ r = EINVAL;
+ goto cleanup;
+ }
+ if (!env->i->generate_row_for_put) {
+ r = EINVAL;
+ goto cleanup;
+ }
+
+ HANDLE_ILLEGAL_WORKING_PARENT_TXN(env, txn);
+ r = get_indexer_if_exists(num_dbs, db_array, src_db, &indexer, &src_same);
+ if (r) {
+ goto cleanup;
+ }
+
+ for (uint32_t which_db = 0; which_db < num_dbs; which_db++) {
+ DB *db = db_array[which_db];
+
+ lock_flags[which_db] = get_prelocked_flags(flags_array[which_db]);
+ remaining_flags[which_db] = flags_array[which_db] & ~lock_flags[which_db];
+
+ //Generate the row
+ if (db == src_db) {
+ put_keys[which_db].size = put_keys[which_db].capacity = 1;
+ put_keys[which_db].dbts = const_cast<DBT*>(src_key);
+
+ put_vals[which_db].size = put_vals[which_db].capacity = 1;
+ put_vals[which_db].dbts = const_cast<DBT*>(src_val);
+ }
+ else {
+ r = env->i->generate_row_for_put(db, src_db, &keys[which_db], &vals[which_db], src_key, src_val);
+ if (r != 0) goto cleanup;
+
+ paranoid_invariant(keys[which_db].size <= keys[which_db].capacity);
+ paranoid_invariant(vals[which_db].size <= vals[which_db].capacity);
+ paranoid_invariant(keys[which_db].size == vals[which_db].size);
+
+ put_keys[which_db] = keys[which_db];
+ put_vals[which_db] = vals[which_db];
+ }
+ for (uint32_t i = 0; i < put_keys[which_db].size; i++) {
+ DBT &put_key = put_keys[which_db].dbts[i];
+ DBT &put_val = put_vals[which_db].dbts[i];
+
+ // check size constraints
+ r = db_put_check_size_constraints(db, &put_key, &put_val);
+ if (r != 0) goto cleanup;
+
+ if (remaining_flags[which_db] == DB_NOOVERWRITE_NO_ERROR) {
+ //put_multiple does not support delaying the no error, since we would
+ //have to log the flag in the put_multiple.
+ r = EINVAL; goto cleanup;
+ }
+
+ //Do locking if necessary.
+ if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) {
+ //Needs locking
+ r = toku_db_get_point_write_lock(db, txn, &put_key);
+ if (r != 0) goto cleanup;
+ }
+ }
+ fts[which_db] = db->i->ft_handle;
+ }
+
+ if (indexer) {
+ // do a cheap check
+ if (src_same) {
+ bool may_insert = toku_indexer_may_insert(indexer, src_key);
+ if (!may_insert) {
+ toku_indexer_lock(indexer);
+ indexer_lock_taken = true;
+ }
+ else {
+ indexer_shortcut = true;
+ }
+ }
+ }
+ toku_multi_operation_client_lock();
+ r = do_put_multiple(txn, num_dbs, db_array, put_keys, put_vals, remaining_flags, src_db, src_key, indexer_shortcut);
+ if (r == 0) {
+ log_put_multiple(txn, src_db, src_key, src_val, num_dbs, fts);
+ }
+ toku_multi_operation_client_unlock();
+ if (indexer_lock_taken) {
+ toku_indexer_unlock(indexer);
+ }
+
+cleanup:
+ if (r == 0)
+ STATUS_VALUE(YDB_LAYER_NUM_MULTI_INSERTS) += num_dbs; // accountability
+ else
+ STATUS_VALUE(YDB_LAYER_NUM_MULTI_INSERTS_FAIL) += num_dbs; // accountability
+ return r;
+}
+
+static void swap_dbts(DBT *a, DBT *b) {
+ DBT c;
+ c = *a;
+ *a = *b;
+ *b = c;
+}
+
+//TODO: 26 Add comment in API description about.. new val.size being generated as '0' REQUIRES old_val.size == 0
+//
+int
+env_update_multiple(DB_ENV *env, DB *src_db, DB_TXN *txn,
+ DBT *old_src_key, DBT *old_src_data,
+ DBT *new_src_key, DBT *new_src_data,
+ uint32_t num_dbs, DB **db_array, uint32_t* flags_array,
+ uint32_t num_keys, DBT_ARRAY keys[],
+ uint32_t num_vals, DBT_ARRAY vals[]) {
+ int r = 0;
+
+ HANDLE_PANICKED_ENV(env);
+ DB_INDEXER* indexer = NULL;
+ bool indexer_shortcut = false;
+ bool indexer_lock_taken = false;
+ bool src_same = false;
+ HANDLE_READ_ONLY_TXN(txn);
+ DBT_ARRAY old_key_arrays[num_dbs];
+ DBT_ARRAY new_key_arrays[num_dbs];
+ DBT_ARRAY new_val_arrays[num_dbs];
+
+ if (!txn) {
+ r = EINVAL;
+ goto cleanup;
+ }
+ if (!env->i->generate_row_for_put) {
+ r = EINVAL;
+ goto cleanup;
+ }
+
+ if (num_dbs + num_dbs > num_keys || num_dbs > num_vals) {
+ r = ENOMEM; goto cleanup;
+ }
+
+ HANDLE_ILLEGAL_WORKING_PARENT_TXN(env, txn);
+ r = get_indexer_if_exists(num_dbs, db_array, src_db, &indexer, &src_same);
+ if (r) {
+ goto cleanup;
+ }
+
+ {
+ uint32_t n_del_dbs = 0;
+ DB *del_dbs[num_dbs];
+ FT_HANDLE del_fts[num_dbs];
+ DBT_ARRAY del_key_arrays[num_dbs];
+
+ uint32_t n_put_dbs = 0;
+ DB *put_dbs[num_dbs];
+ FT_HANDLE put_fts[num_dbs];
+ DBT_ARRAY put_key_arrays[num_dbs];
+ DBT_ARRAY put_val_arrays[num_dbs];
+
+ uint32_t lock_flags[num_dbs];
+ uint32_t remaining_flags[num_dbs];
+
+ for (uint32_t which_db = 0; which_db < num_dbs; which_db++) {
+ DB *db = db_array[which_db];
+
+ lock_flags[which_db] = get_prelocked_flags(flags_array[which_db]);
+ remaining_flags[which_db] = flags_array[which_db] & ~lock_flags[which_db];
+
+ if (db == src_db) {
+ // Copy the old keys
+ old_key_arrays[which_db].size = old_key_arrays[which_db].capacity = 1;
+ old_key_arrays[which_db].dbts = old_src_key;
+
+ // Copy the new keys and vals
+ new_key_arrays[which_db].size = new_key_arrays[which_db].capacity = 1;
+ new_key_arrays[which_db].dbts = new_src_key;
+
+ new_val_arrays[which_db].size = new_val_arrays[which_db].capacity = 1;
+ new_val_arrays[which_db].dbts = new_src_data;
+ } else {
+ // keys[0..num_dbs-1] are the new keys
+ // keys[num_dbs..2*num_dbs-1] are the old keys
+ // vals[0..num_dbs-1] are the new vals
+
+ // Generate the old keys
+ r = env->i->generate_row_for_put(db, src_db, &keys[which_db + num_dbs], NULL, old_src_key, old_src_data);
+ if (r != 0) goto cleanup;
+
+ paranoid_invariant(keys[which_db+num_dbs].size <= keys[which_db+num_dbs].capacity);
+ old_key_arrays[which_db] = keys[which_db+num_dbs];
+
+ // Generate the new keys and vals
+ r = env->i->generate_row_for_put(db, src_db, &keys[which_db], &vals[which_db], new_src_key, new_src_data);
+ if (r != 0) goto cleanup;
+
+ paranoid_invariant(keys[which_db].size <= keys[which_db].capacity);
+ paranoid_invariant(vals[which_db].size <= vals[which_db].capacity);
+ paranoid_invariant(keys[which_db].size == vals[which_db].size);
+
+ new_key_arrays[which_db] = keys[which_db];
+ new_val_arrays[which_db] = vals[which_db];
+ }
+ DBT_ARRAY &old_keys = old_key_arrays[which_db];
+ DBT_ARRAY &new_keys = new_key_arrays[which_db];
+ DBT_ARRAY &new_vals = new_val_arrays[which_db];
+
+ uint32_t num_skip = 0;
+ uint32_t num_del = 0;
+ uint32_t num_put = 0;
+ // Next index in old_keys to look at
+ uint32_t idx_old = 0;
+ // Next index in new_keys/new_vals to look at
+ uint32_t idx_new = 0;
+ uint32_t idx_old_used = 0;
+ uint32_t idx_new_used = 0;
+ while (idx_old < old_keys.size || idx_new < new_keys.size) {
+ // Check for old key, both, new key
+ DBT *curr_old_key = &old_keys.dbts[idx_old];
+ DBT *curr_new_key = &new_keys.dbts[idx_new];
+ DBT *curr_new_val = &new_vals.dbts[idx_new];
+
+ bool locked_new_key = false;
+ int cmp;
+ if (idx_new == new_keys.size) {
+ cmp = -1;
+ } else if (idx_old == old_keys.size) {
+ cmp = +1;
+ } else {
+ const toku::comparator &cmpfn = toku_db_get_comparator(db);
+ cmp = cmpfn(curr_old_key, curr_new_key);
+ }
+
+ bool do_del = false;
+ bool do_put = false;
+ bool do_skip = false;
+ if (cmp > 0) { // New key does not exist in old array
+ //Check overwrite constraints only in the case where the keys are not equal
+ //(new key is alone/not equal to old key)
+ // If the keys are equal, then we do not care of the flag is DB_NOOVERWRITE or 0
+ r = db_put_check_overwrite_constraint(db, txn,
+ curr_new_key,
+ lock_flags[which_db], remaining_flags[which_db]);
+ if (r != 0) goto cleanup;
+ if (remaining_flags[which_db] == DB_NOOVERWRITE) {
+ locked_new_key = true;
+ }
+
+ if (remaining_flags[which_db] == DB_NOOVERWRITE_NO_ERROR) {
+ //update_multiple does not support delaying the no error, since we would
+ //have to log the flag in the put_multiple.
+ r = EINVAL; goto cleanup;
+ }
+ do_put = true;
+ } else if (cmp < 0) {
+ // lock old key only when it does not exist in new array
+ // otherwise locking new key takes care of this
+ if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE)) {
+ r = toku_db_get_point_write_lock(db, txn, curr_old_key);
+ if (r != 0) goto cleanup;
+ }
+ do_del = true;
+ } else {
+ do_put = curr_new_val->size > 0 ||
+ curr_old_key->size != curr_new_key->size ||
+ memcmp(curr_old_key->data, curr_new_key->data, curr_old_key->size);
+ do_skip = !do_put;
+ }
+ // Check put size constraints and insert new key only if keys are unequal (byte for byte) or there is a val
+ // We assume any val.size > 0 as unequal (saves on generating old val)
+ // (allows us to avoid generating the old val)
+ // we assume that any new vals with size > 0 are different than the old val
+ // if (!key_eq || !(dbt_cmp(&vals[which_db], &vals[which_db + num_dbs]) == 0)) { /* ... */ }
+ if (do_put) {
+ r = db_put_check_size_constraints(db, curr_new_key, curr_new_val);
+ if (r != 0) goto cleanup;
+
+ // lock new key unless already locked
+ if (db->i->lt && !(lock_flags[which_db] & DB_PRELOCKED_WRITE) && !locked_new_key) {
+ r = toku_db_get_point_write_lock(db, txn, curr_new_key);
+ if (r != 0) goto cleanup;
+ }
+ }
+
+ // TODO: 26 Add comments explaining squish and why not just use another stack array
+ // Add more comments to explain this if elseif else well
+ if (do_skip) {
+ paranoid_invariant(cmp == 0);
+ paranoid_invariant(!do_put);
+ paranoid_invariant(!do_del);
+
+ num_skip++;
+ idx_old++;
+ idx_new++;
+ } else if (do_put) {
+ paranoid_invariant(cmp >= 0);
+ paranoid_invariant(!do_skip);
+ paranoid_invariant(!do_del);
+
+ num_put++;
+ if (idx_new != idx_new_used) {
+ swap_dbts(&new_keys.dbts[idx_new_used], &new_keys.dbts[idx_new]);
+ swap_dbts(&new_vals.dbts[idx_new_used], &new_vals.dbts[idx_new]);
+ }
+ idx_new++;
+ idx_new_used++;
+ if (cmp == 0) {
+ idx_old++;
+ }
+ } else {
+ invariant(do_del);
+ paranoid_invariant(cmp < 0);
+ paranoid_invariant(!do_skip);
+ paranoid_invariant(!do_put);
+
+ num_del++;
+ if (idx_old != idx_old_used) {
+ swap_dbts(&old_keys.dbts[idx_old_used], &old_keys.dbts[idx_old]);
+ }
+ idx_old++;
+ idx_old_used++;
+ }
+ }
+ old_keys.size = idx_old_used;
+ new_keys.size = idx_new_used;
+ new_vals.size = idx_new_used;
+
+ if (num_del > 0) {
+ del_dbs[n_del_dbs] = db;
+ del_fts[n_del_dbs] = db->i->ft_handle;
+ del_key_arrays[n_del_dbs] = old_keys;
+ n_del_dbs++;
+ }
+ // If we put none, but delete some, but not all, then we need the log_put_multiple to happen.
+ // Include this db in the put_dbs so we do log_put_multiple.
+ // do_put_multiple will be a no-op for this db.
+ if (num_put > 0 || (num_del > 0 && num_skip > 0)) {
+ put_dbs[n_put_dbs] = db;
+ put_fts[n_put_dbs] = db->i->ft_handle;
+ put_key_arrays[n_put_dbs] = new_keys;
+ put_val_arrays[n_put_dbs] = new_vals;
+ n_put_dbs++;
+ }
+ }
+ if (indexer) {
+ // do a cheap check
+ if (src_same) {
+ bool may_insert =
+ toku_indexer_may_insert(indexer, old_src_key) &&
+ toku_indexer_may_insert(indexer, new_src_key);
+ if (!may_insert) {
+ toku_indexer_lock(indexer);
+ indexer_lock_taken = true;
+ }
+ else {
+ indexer_shortcut = true;
+ }
+ }
+ }
+ toku_multi_operation_client_lock();
+ if (r == 0 && n_del_dbs > 0) {
+ log_del_multiple(txn, src_db, old_src_key, old_src_data, n_del_dbs, del_fts, del_key_arrays);
+ r = do_del_multiple(txn, n_del_dbs, del_dbs, del_key_arrays, src_db, old_src_key, indexer_shortcut);
+ }
+
+ if (r == 0 && n_put_dbs > 0) {
+ // We sometimes skip some keys for del/put during runtime, but during recovery
+ // we (may) delete ALL the keys for a given DB. Therefore we must put ALL the keys during
+ // recovery so we don't end up losing data.
+ // So unlike env->put_multiple, we ONLY log a 'put_multiple' log entry.
+ log_put_multiple(txn, src_db, new_src_key, new_src_data, n_put_dbs, put_fts);
+ r = do_put_multiple(txn, n_put_dbs, put_dbs, put_key_arrays, put_val_arrays, nullptr, src_db, new_src_key, indexer_shortcut);
+ }
+ toku_multi_operation_client_unlock();
+ if (indexer_lock_taken) {
+ toku_indexer_unlock(indexer);
+ }
+ }
+
+cleanup:
+ if (r == 0)
+ STATUS_VALUE(YDB_LAYER_NUM_MULTI_UPDATES) += num_dbs; // accountability
+ else
+ STATUS_VALUE(YDB_LAYER_NUM_MULTI_UPDATES_FAIL) += num_dbs; // accountability
+ return r;
+}
+
+int
+autotxn_db_del(DB* db, DB_TXN* txn, DBT* key, uint32_t flags) {
+ bool changed; int r;
+ r = toku_db_construct_autotxn(db, &txn, &changed, false);
+ if (r!=0) return r;
+ r = toku_db_del(db, txn, key, flags, false);
+ return toku_db_destruct_autotxn(txn, r, changed);
+}
+
+int
+autotxn_db_put(DB* db, DB_TXN* txn, DBT* key, DBT* data, uint32_t flags) {
+ //{ unsigned i; printf("put %p keylen=%d key={", db, key->size); for(i=0; i<key->size; i++) printf("%d,", ((char*)key->data)[i]); printf("} datalen=%d data={", data->size); for(i=0; i<data->size; i++) printf("%d,", ((char*)data->data)[i]); printf("}\n"); }
+ bool changed; int r;
+ r = env_check_avail_fs_space(db->dbenv);
+ if (r != 0) { goto cleanup; }
+ r = toku_db_construct_autotxn(db, &txn, &changed, false);
+ if (r!=0) {
+ goto cleanup;
+ }
+ r = toku_db_put(db, txn, key, data, flags, false);
+ r = toku_db_destruct_autotxn(txn, r, changed);
+cleanup:
+ return r;
+}
+
+int
+autotxn_db_update(DB *db, DB_TXN *txn,
+ const DBT *key,
+ const DBT *update_function_extra,
+ uint32_t flags) {
+ bool changed; int r;
+ r = env_check_avail_fs_space(db->dbenv);
+ if (r != 0) { goto cleanup; }
+ r = toku_db_construct_autotxn(db, &txn, &changed, false);
+ if (r != 0) { return r; }
+ r = toku_db_update(db, txn, key, update_function_extra, flags);
+ r = toku_db_destruct_autotxn(txn, r, changed);
+cleanup:
+ return r;
+}
+
+int
+autotxn_db_update_broadcast(DB *db, DB_TXN *txn,
+ const DBT *update_function_extra,
+ uint32_t flags) {
+ bool changed; int r;
+ r = env_check_avail_fs_space(db->dbenv);
+ if (r != 0) { goto cleanup; }
+ r = toku_db_construct_autotxn(db, &txn, &changed, false);
+ if (r != 0) { return r; }
+ r = toku_db_update_broadcast(db, txn, update_function_extra, flags);
+ r = toku_db_destruct_autotxn(txn, r, changed);
+cleanup:
+ return r;
+}
+
+int
+env_put_multiple(DB_ENV *env, DB *src_db, DB_TXN *txn, const DBT *src_key, const DBT *src_val, uint32_t num_dbs, DB **db_array, DBT_ARRAY *keys, DBT_ARRAY *vals, uint32_t *flags_array) {
+ int r = env_check_avail_fs_space(env);
+ if (r == 0) {
+ r = env_put_multiple_internal(env, src_db, txn, src_key, src_val, num_dbs, db_array, keys, vals, flags_array);
+ }
+ return r;
+}
+
+int
+toku_ydb_check_avail_fs_space(DB_ENV *env) {
+ int rval = env_check_avail_fs_space(env);
+ return rval;
+}
+#undef STATUS_VALUE
+
+#include <toku_race_tools.h>
+void __attribute__((constructor)) toku_ydb_write_helgrind_ignore(void);
+void
+toku_ydb_write_helgrind_ignore(void) {
+ TOKU_VALGRIND_HG_DISABLE_CHECKING(&ydb_write_layer_status, sizeof ydb_write_layer_status);
+}
diff --git a/storage/tokudb/PerconaFT/src/ydb_write.h b/storage/tokudb/PerconaFT/src/ydb_write.h
new file mode 100644
index 00000000000..502b60c0b74
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/ydb_write.h
@@ -0,0 +1,104 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+/*======
+This file is part of PerconaFT.
+
+
+Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License, version 2,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+
+----------------------------------------
+
+ PerconaFT is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License, version 3,
+ as published by the Free Software Foundation.
+
+ PerconaFT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
+======= */
+
+#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
+
+#pragma once
+
+typedef enum {
+ YDB_LAYER_NUM_INSERTS = 0,
+ YDB_LAYER_NUM_INSERTS_FAIL,
+ YDB_LAYER_NUM_DELETES,
+ YDB_LAYER_NUM_DELETES_FAIL,
+ YDB_LAYER_NUM_UPDATES,
+ YDB_LAYER_NUM_UPDATES_FAIL,
+ YDB_LAYER_NUM_UPDATES_BROADCAST,
+ YDB_LAYER_NUM_UPDATES_BROADCAST_FAIL,
+ YDB_LAYER_NUM_MULTI_INSERTS,
+ YDB_LAYER_NUM_MULTI_INSERTS_FAIL,
+ YDB_LAYER_NUM_MULTI_DELETES,
+ YDB_LAYER_NUM_MULTI_DELETES_FAIL,
+ YDB_LAYER_NUM_MULTI_UPDATES,
+ YDB_LAYER_NUM_MULTI_UPDATES_FAIL,
+ YDB_WRITE_LAYER_STATUS_NUM_ROWS /* number of rows in this status array */
+} ydb_write_lock_layer_status_entry;
+
+typedef struct {
+ bool initialized;
+ TOKU_ENGINE_STATUS_ROW_S status[YDB_WRITE_LAYER_STATUS_NUM_ROWS];
+} YDB_WRITE_LAYER_STATUS_S, *YDB_WRITE_LAYER_STATUS;
+
+void ydb_write_layer_get_status(YDB_WRITE_LAYER_STATUS statp);
+
+int toku_db_del(DB *db, DB_TXN *txn, DBT *key, uint32_t flags, bool holds_mo_lock);
+int toku_db_put(DB *db, DB_TXN *txn, DBT *key, DBT *val, uint32_t flags, bool holds_mo_lock);
+int autotxn_db_del(DB* db, DB_TXN* txn, DBT* key, uint32_t flags);
+int autotxn_db_put(DB* db, DB_TXN* txn, DBT* key, DBT* data, uint32_t flags);
+int autotxn_db_update(DB *db, DB_TXN *txn, const DBT *key, const DBT *update_function_extra, uint32_t flags);
+int autotxn_db_update_broadcast(DB *db, DB_TXN *txn, const DBT *update_function_extra, uint32_t flags);
+int env_put_multiple(
+ DB_ENV *env,
+ DB *src_db,
+ DB_TXN *txn,
+ const DBT *src_key, const DBT *src_val,
+ uint32_t num_dbs,
+ DB **db_array,
+ DBT_ARRAY *keys, DBT_ARRAY *vals,
+ uint32_t *flags_array
+ );
+int env_del_multiple(
+ DB_ENV *env,
+ DB *src_db,
+ DB_TXN *txn,
+ const DBT *src_key,
+ const DBT *src_val,
+ uint32_t num_dbs,
+ DB **db_array,
+ DBT_ARRAY *keys,
+ uint32_t *flags_array
+ );
+int env_update_multiple(
+ DB_ENV *env,
+ DB *src_db,
+ DB_TXN *txn,
+ DBT *old_src_key, DBT *old_src_data,
+ DBT *new_src_key, DBT *new_src_data,
+ uint32_t num_dbs,
+ DB **db_array,
+ uint32_t* flags_array,
+ uint32_t num_keys, DBT_ARRAY keys[],
+ uint32_t num_vals, DBT_ARRAY vals[]
+ );