tdb2: transaction support
[ccan] / ccan / tdb2 / test / run-firstkey-nextkey.c
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
12 #define NUM_RECORDS 1000
13
14 static bool store_records(struct tdb_context *tdb)
15 {
16         int i;
17         struct tdb_data key = { (unsigned char *)&i, sizeof(i) };
18         struct tdb_data data = { (unsigned char *)&i, sizeof(i) };
19
20         for (i = 0; i < NUM_RECORDS; i++)
21                 if (tdb_store(tdb, key, data, TDB_REPLACE) != 0)
22                         return false;
23         return true;
24 }
25
26 struct trav_data {
27         unsigned int records[NUM_RECORDS];
28         unsigned int calls;
29 };
30
31 static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *p)
32 {
33         struct trav_data *td = p;
34         int val;
35
36         memcpy(&val, dbuf.dptr, dbuf.dsize);
37         td->records[td->calls++] = val;
38         return 0;
39 }
40
41 int main(int argc, char *argv[])
42 {
43         unsigned int i, j;
44         int num;
45         struct trav_data td;
46         TDB_DATA k, k2;
47         struct tdb_context *tdb;
48         union tdb_attribute seed_attr;
49
50         int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
51                         TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, 
52                         TDB_NOMMAP|TDB_CONVERT };
53
54         seed_attr.base.attr = TDB_ATTRIBUTE_SEED;
55         seed_attr.base.next = &tap_log_attr;
56         seed_attr.seed.seed = 6334326220117065685ULL;
57
58         plan_tests(sizeof(flags) / sizeof(flags[0])
59                    * (NUM_RECORDS*4 + (NUM_RECORDS-1)*2 + 20) + 1);
60         for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
61                 tdb = tdb_open("run-traverse.tdb", flags[i],
62                                O_RDWR|O_CREAT|O_TRUNC, 0600, &seed_attr);
63                 ok1(tdb);
64                 if (!tdb)
65                         continue;
66
67                 ok1(tdb_firstkey(tdb).dptr == NULL);
68                 ok1(tdb_error(tdb) == TDB_SUCCESS);
69
70                 /* One entry... */
71                 k.dptr = (unsigned char *)&num;
72                 k.dsize = sizeof(num);
73                 num = 0;
74                 ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0);
75                 k = tdb_firstkey(tdb);
76                 ok1(k.dsize == sizeof(num));
77                 ok1(memcmp(k.dptr, &num, sizeof(num)) == 0);
78                 k2 = tdb_nextkey(tdb, k);
79                 ok1(k2.dsize == 0 && k2.dptr == NULL);
80                 free(k.dptr);
81
82                 /* Two entries. */
83                 k.dptr = (unsigned char *)&num;
84                 k.dsize = sizeof(num);
85                 num = 1;
86                 ok1(tdb_store(tdb, k, k, TDB_INSERT) == 0);
87                 k = tdb_firstkey(tdb);
88                 ok1(k.dsize == sizeof(num));
89                 memcpy(&num, k.dptr, sizeof(num));
90                 ok1(num == 0 || num == 1);
91                 k2 = tdb_nextkey(tdb, k);
92                 ok1(k2.dsize == sizeof(j));
93                 free(k.dptr);
94                 memcpy(&j, k2.dptr, sizeof(j));
95                 ok1(j == 0 || j == 1);
96                 ok1(j != num);
97                 k = tdb_nextkey(tdb, k2);
98                 ok1(k.dsize == 0 && k.dptr == NULL);
99                 free(k2.dptr);
100
101                 /* Clean up. */
102                 k.dptr = (unsigned char *)&num;
103                 k.dsize = sizeof(num);
104                 num = 0;
105                 ok1(tdb_delete(tdb, k) == 0);
106                 num = 1;
107                 ok1(tdb_delete(tdb, k) == 0);
108
109                 /* Now lots of records. */
110                 ok1(store_records(tdb));
111                 td.calls = 0;
112
113                 num = tdb_traverse_read(tdb, trav, &td);
114                 ok1(num == NUM_RECORDS);
115                 ok1(td.calls == NUM_RECORDS);
116
117                 /* Simple loop should match tdb_traverse_read */
118                 for (j = 0, k = tdb_firstkey(tdb); j < td.calls; j++) {
119                         int val;
120
121                         ok1(k.dsize == sizeof(val));
122                         memcpy(&val, k.dptr, k.dsize);
123                         ok1(td.records[j] == val);
124                         k2 = tdb_nextkey(tdb, k);
125                         free(k.dptr);
126                         k = k2;
127                 }
128
129                 /* But arbitary orderings should work too. */
130                 for (j = td.calls-1; j > 0; j--) {
131                         k.dptr = (unsigned char *)&td.records[j-1];
132                         k.dsize = sizeof(td.records[j-1]);
133                         k = tdb_nextkey(tdb, k);
134                         ok1(k.dsize == sizeof(td.records[j]));
135                         ok1(memcmp(k.dptr, &td.records[j], k.dsize) == 0);
136                         free(k.dptr);
137                 }
138
139                 /* Even delete should work. */
140                 for (j = 0, k = tdb_firstkey(tdb); k.dptr; j++) {
141                         ok1(k.dsize == 4);
142                         ok1(tdb_delete(tdb, k) == 0);
143                         k2 = tdb_nextkey(tdb, k);
144                         free(k.dptr);
145                         k = k2;
146                 }
147
148                 diag("delete using first/nextkey gave %u of %u records",
149                      j, NUM_RECORDS);
150                 ok1(j == NUM_RECORDS);
151                 tdb_close(tdb);
152         }
153
154         ok1(tap_log_messages == 0);
155         return exit_status();
156 }