run-traverse.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #include <ccan/tdb2/tdb.c>
  2. #include <ccan/tdb2/free.c>
  3. #include <ccan/tdb2/lock.c>
  4. #include <ccan/tdb2/io.c>
  5. #include <ccan/tdb2/hash.c>
  6. #include <ccan/tdb2/check.c>
  7. #include <ccan/tdb2/traverse.c>
  8. #include <ccan/tdb2/transaction.c>
  9. #include <ccan/tap/tap.h>
  10. #include "logging.h"
  11. #define NUM_RECORDS 1000
  12. /* We use the same seed which we saw a failure on. */
  13. static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p)
  14. {
  15. return hash64_stable((const unsigned char *)key, len,
  16. *(uint64_t *)p);
  17. }
  18. static bool store_records(struct tdb_context *tdb)
  19. {
  20. int i;
  21. struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
  22. struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
  23. for (i = 0; i < NUM_RECORDS; i++)
  24. if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
  25. return false;
  26. return true;
  27. }
  28. struct trav_data {
  29. unsigned int calls, call_limit;
  30. int low, high;
  31. bool mismatch;
  32. bool delete;
  33. enum TDB_ERROR delete_error;
  34. };
  35. static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
  36. struct trav_data *td)
  37. {
  38. int val;
  39. td->calls++;
  40. if (key.dsize != sizeof(val) || dbuf.dsize != sizeof(val)
  41. || memcmp(key.dptr, dbuf.dptr, key.dsize) != 0) {
  42. td->mismatch = true;
  43. return -1;
  44. }
  45. memcpy(&val, dbuf.dptr, dbuf.dsize);
  46. if (val < td->low)
  47. td->low = val;
  48. if (val > td->high)
  49. td->high = val;
  50. if (td->delete) {
  51. td->delete_error = tdb_delete(tdb, key);
  52. if (td->delete_error != TDB_SUCCESS) {
  53. return -1;
  54. }
  55. }
  56. if (td->calls == td->call_limit)
  57. return 1;
  58. return 0;
  59. }
  60. struct trav_grow_data {
  61. unsigned int calls;
  62. unsigned int num_large;
  63. bool mismatch;
  64. enum TDB_ERROR error;
  65. };
  66. static int trav_grow(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
  67. struct trav_grow_data *tgd)
  68. {
  69. int val;
  70. unsigned char buffer[128] = { 0 };
  71. tgd->calls++;
  72. if (key.dsize != sizeof(val) || dbuf.dsize < sizeof(val)
  73. || memcmp(key.dptr, dbuf.dptr, key.dsize) != 0) {
  74. tgd->mismatch = true;
  75. return -1;
  76. }
  77. if (dbuf.dsize > sizeof(val))
  78. /* We must have seen this before! */
  79. tgd->num_large++;
  80. /* Make a big difference to the database. */
  81. dbuf.dptr = buffer;
  82. dbuf.dsize = sizeof(buffer);
  83. tgd->error = tdb_append(tdb, key, dbuf);
  84. if (tgd->error != TDB_SUCCESS) {
  85. return -1;
  86. }
  87. return 0;
  88. }
  89. int main(int argc, char *argv[])
  90. {
  91. unsigned int i;
  92. int num;
  93. struct trav_data td;
  94. struct trav_grow_data tgd;
  95. struct tdb_context *tdb;
  96. uint64_t seed = 16014841315512641303ULL;
  97. int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
  98. TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT,
  99. TDB_NOMMAP|TDB_CONVERT };
  100. union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH },
  101. .hash_fn = fixedhash,
  102. .hash_private = &seed } };
  103. hattr.base.next = &tap_log_attr;
  104. plan_tests(sizeof(flags) / sizeof(flags[0]) * 32 + 1);
  105. for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
  106. tdb = tdb_open("run-traverse.tdb", flags[i],
  107. O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
  108. ok1(tdb);
  109. if (!tdb)
  110. continue;
  111. ok1(tdb_traverse(tdb, NULL, NULL) == 0);
  112. ok1(store_records(tdb));
  113. num = tdb_traverse(tdb, NULL, NULL);
  114. ok1(num == NUM_RECORDS);
  115. /* Full traverse. */
  116. td.calls = 0;
  117. td.call_limit = UINT_MAX;
  118. td.low = INT_MAX;
  119. td.high = INT_MIN;
  120. td.mismatch = false;
  121. td.delete = false;
  122. num = tdb_traverse(tdb, trav, &td);
  123. ok1(num == NUM_RECORDS);
  124. ok1(!td.mismatch);
  125. ok1(td.calls == NUM_RECORDS);
  126. ok1(td.low == 0);
  127. ok1(td.high == NUM_RECORDS-1);
  128. /* Short traverse. */
  129. td.calls = 0;
  130. td.call_limit = NUM_RECORDS / 2;
  131. td.low = INT_MAX;
  132. td.high = INT_MIN;
  133. td.mismatch = false;
  134. td.delete = false;
  135. num = tdb_traverse(tdb, trav, &td);
  136. ok1(num == NUM_RECORDS / 2);
  137. ok1(!td.mismatch);
  138. ok1(td.calls == NUM_RECORDS / 2);
  139. ok1(td.low <= NUM_RECORDS / 2);
  140. ok1(td.high > NUM_RECORDS / 2);
  141. ok1(tdb_check(tdb, NULL, NULL) == 0);
  142. ok1(tap_log_messages == 0);
  143. /* Deleting traverse (delete everything). */
  144. td.calls = 0;
  145. td.call_limit = UINT_MAX;
  146. td.low = INT_MAX;
  147. td.high = INT_MIN;
  148. td.mismatch = false;
  149. td.delete = true;
  150. td.delete_error = TDB_SUCCESS;
  151. num = tdb_traverse(tdb, trav, &td);
  152. ok1(num == NUM_RECORDS);
  153. ok1(td.delete_error == TDB_SUCCESS);
  154. ok1(!td.mismatch);
  155. ok1(td.calls == NUM_RECORDS);
  156. ok1(td.low == 0);
  157. ok1(td.high == NUM_RECORDS - 1);
  158. ok1(tdb_check(tdb, NULL, NULL) == 0);
  159. /* Now it's empty! */
  160. ok1(tdb_traverse(tdb, NULL, NULL) == 0);
  161. /* Re-add. */
  162. ok1(store_records(tdb));
  163. ok1(tdb_traverse(tdb, NULL, NULL) == NUM_RECORDS);
  164. ok1(tdb_check(tdb, NULL, NULL) == 0);
  165. /* Grow. This will cause us to be reshuffled. */
  166. tgd.calls = 0;
  167. tgd.num_large = 0;
  168. tgd.mismatch = false;
  169. tgd.error = TDB_SUCCESS;
  170. ok1(tdb_traverse(tdb, trav_grow, &tgd) > 1);
  171. ok1(tgd.error == 0);
  172. ok1(!tgd.mismatch);
  173. ok1(tdb_check(tdb, NULL, NULL) == 0);
  174. ok1(tgd.num_large < tgd.calls);
  175. diag("growing db: %u calls, %u repeats",
  176. tgd.calls, tgd.num_large);
  177. tdb_close(tdb);
  178. }
  179. ok1(tap_log_messages == 0);
  180. return exit_status();
  181. }