1 #include "ntdb-source.h"
2 #include "tap-interface.h"
5 /* We rig the hash so all records clash. */
6 static uint32_t clash(const void *key
, size_t len
, uint32_t seed
, void *priv
)
8 return *((const unsigned int *)key
) << 20;
11 int main(int argc
, char *argv
[])
14 struct ntdb_context
*ntdb
;
16 struct ntdb_used_record rec
;
17 NTDB_DATA key
= { (unsigned char *)&v
, sizeof(v
) };
18 NTDB_DATA dbuf
= { (unsigned char *)&v
, sizeof(v
) };
19 union ntdb_attribute hattr
= { .hash
= { .base
= { NTDB_ATTRIBUTE_HASH
},
21 int flags
[] = { NTDB_INTERNAL
, NTDB_DEFAULT
, NTDB_NOMMAP
,
22 NTDB_INTERNAL
|NTDB_CONVERT
, NTDB_CONVERT
,
23 NTDB_NOMMAP
|NTDB_CONVERT
,
26 hattr
.base
.next
= &tap_log_attr
;
28 plan_tests(sizeof(flags
) / sizeof(flags
[0]) * 137 + 1);
29 for (i
= 0; i
< sizeof(flags
) / sizeof(flags
[0]); i
++) {
31 ntdb_off_t new_off
, new_off2
, off
;
33 ntdb
= ntdb_open("run-04-basichash.ntdb", flags
[i
]|MAYBE_NOSYNC
,
34 O_RDWR
|O_CREAT
|O_TRUNC
, 0600, &hattr
);
40 /* Should not find it. */
41 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == 0);
42 /* Should have created correct hash. */
43 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
44 /* Should have located space in top table, bucket 0. */
45 ok1(h
.table
== NTDB_HASH_OFFSET
);
46 ok1(h
.table_size
== (1 << ntdb
->hash_bits
));
50 /* Should have lock on bucket 0 */
52 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
53 ok1((ntdb
->flags
& NTDB_NOLOCK
)
54 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
55 /* FIXME: Check lock length */
57 /* Allocate a new record. */
58 new_off
= alloc(ntdb
, key
.dsize
, dbuf
.dsize
,
59 NTDB_USED_MAGIC
, false);
60 ok1(!NTDB_OFF_IS_ERR(new_off
));
62 /* We should be able to add it now. */
63 ok1(add_to_hash(ntdb
, &h
, new_off
) == 0);
65 /* Make sure we fill it in for later finding. */
66 off
= new_off
+ sizeof(struct ntdb_used_record
);
67 ok1(!ntdb
->io
->twrite(ntdb
, off
, key
.dptr
, key
.dsize
));
69 ok1(!ntdb
->io
->twrite(ntdb
, off
, dbuf
.dptr
, dbuf
.dsize
));
71 /* We should be able to unlock that OK. */
72 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
74 /* Database should be consistent. */
75 ok1(ntdb_check(ntdb
, NULL
, NULL
) == 0);
77 /* Now, this should give a successful lookup. */
78 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == new_off
);
79 /* Should have created correct hash. */
80 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
81 /* Should have located it in top table, bucket 0. */
82 ok1(h
.table
== NTDB_HASH_OFFSET
);
83 ok1(h
.table_size
== (1 << ntdb
->hash_bits
));
86 /* Should have lock on bucket 0 */
88 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
89 ok1((ntdb
->flags
& NTDB_NOLOCK
)
90 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
91 /* FIXME: Check lock length */
93 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
95 /* Database should be consistent. */
96 ok1(ntdb_check(ntdb
, NULL
, NULL
) == 0);
100 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == 0);
101 /* Should have created correct hash. */
102 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
103 /* Should have located clash in toplevel bucket 0. */
104 ok1(h
.table
== NTDB_HASH_OFFSET
);
105 ok1(h
.table_size
== (1 << ntdb
->hash_bits
));
107 ok1((h
.old_val
& NTDB_OFF_MASK
) == new_off
);
109 /* Should have lock on bucket 0 */
110 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
111 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
112 ok1((ntdb
->flags
& NTDB_NOLOCK
)
113 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
114 /* FIXME: Check lock length */
116 new_off2
= alloc(ntdb
, key
.dsize
, dbuf
.dsize
,
117 NTDB_USED_MAGIC
, false);
118 ok1(!NTDB_OFF_IS_ERR(new_off2
));
120 off
= new_off2
+ sizeof(struct ntdb_used_record
);
121 ok1(!ntdb
->io
->twrite(ntdb
, off
, key
.dptr
, key
.dsize
));
123 ok1(!ntdb
->io
->twrite(ntdb
, off
, dbuf
.dptr
, dbuf
.dsize
));
125 /* We should be able to add it now. */
126 ok1(add_to_hash(ntdb
, &h
, new_off2
) == 0);
127 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
129 /* Should be happy with expansion. */
130 ok1(ntdb_check(ntdb
, NULL
, NULL
) == 0);
132 /* Should be able to find both. */
134 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == new_off2
);
135 /* Should have created correct hash. */
136 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
137 /* Should have located space in chain. */
138 ok1(h
.table
> NTDB_HASH_OFFSET
);
139 ok1(h
.table_size
== 2);
141 /* Should have lock on bucket 0 */
142 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
143 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
144 ok1((ntdb
->flags
& NTDB_NOLOCK
)
145 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
146 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
149 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == new_off
);
150 /* Should have created correct hash. */
151 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
152 /* Should have located space in chain. */
153 ok1(h
.table
> NTDB_HASH_OFFSET
);
154 ok1(h
.table_size
== 2);
157 /* Should have lock on bucket 0 */
158 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
159 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
160 ok1((ntdb
->flags
& NTDB_NOLOCK
)
161 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
162 /* FIXME: Check lock length */
164 /* Simple delete should work. */
165 ok1(delete_from_hash(ntdb
, &h
) == 0);
166 ok1(add_free_record(ntdb
, new_off
,
167 sizeof(struct ntdb_used_record
)
168 + rec_key_length(&rec
)
169 + rec_data_length(&rec
)
170 + rec_extra_padding(&rec
),
171 NTDB_LOCK_NOWAIT
, false) == 0);
172 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
173 ok1(ntdb_check(ntdb
, NULL
, NULL
) == 0);
175 /* Should still be able to find other record. */
177 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == new_off2
);
178 /* Should have created correct hash. */
179 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
180 /* Should have located space in chain. */
181 ok1(h
.table
> NTDB_HASH_OFFSET
);
182 ok1(h
.table_size
== 2);
184 /* Should have lock on bucket 0 */
185 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
186 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
187 ok1((ntdb
->flags
& NTDB_NOLOCK
)
188 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
189 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
191 /* Now should find empty space. */
193 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == 0);
194 /* Should have created correct hash. */
195 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
196 /* Should have located space in chain, bucket 0. */
197 ok1(h
.table
> NTDB_HASH_OFFSET
);
198 ok1(h
.table_size
== 2);
202 /* Adding another record should work. */
204 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == 0);
205 /* Should have created correct hash. */
206 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
207 /* Should have located space in chain, bucket 0. */
208 ok1(h
.table
> NTDB_HASH_OFFSET
);
209 ok1(h
.table_size
== 2);
213 /* Should have lock on bucket 0 */
214 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
215 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
216 ok1((ntdb
->flags
& NTDB_NOLOCK
)
217 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
219 new_off
= alloc(ntdb
, key
.dsize
, dbuf
.dsize
,
220 NTDB_USED_MAGIC
, false);
221 ok1(!NTDB_OFF_IS_ERR(new_off2
));
222 ok1(add_to_hash(ntdb
, &h
, new_off
) == 0);
223 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
225 off
= new_off
+ sizeof(struct ntdb_used_record
);
226 ok1(!ntdb
->io
->twrite(ntdb
, off
, key
.dptr
, key
.dsize
));
228 ok1(!ntdb
->io
->twrite(ntdb
, off
, dbuf
.dptr
, dbuf
.dsize
));
230 /* Adding another record should cause expansion. */
232 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == 0);
233 /* Should have created correct hash. */
234 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
235 /* Should not have located space in chain. */
236 ok1(h
.table
> NTDB_HASH_OFFSET
);
237 ok1(h
.table_size
== 2);
241 /* Should have lock on bucket 0 */
242 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
243 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
244 ok1((ntdb
->flags
& NTDB_NOLOCK
)
245 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
247 new_off
= alloc(ntdb
, key
.dsize
, dbuf
.dsize
,
248 NTDB_USED_MAGIC
, false);
249 ok1(!NTDB_OFF_IS_ERR(new_off2
));
250 off
= new_off
+ sizeof(struct ntdb_used_record
);
251 ok1(!ntdb
->io
->twrite(ntdb
, off
, key
.dptr
, key
.dsize
));
253 ok1(!ntdb
->io
->twrite(ntdb
, off
, dbuf
.dptr
, dbuf
.dsize
));
254 ok1(add_to_hash(ntdb
, &h
, new_off
) == 0);
255 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
257 /* Retrieve it and check. */
258 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == new_off
);
259 /* Should have created correct hash. */
260 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
261 /* Should have appended to chain, bucket 2. */
262 ok1(h
.table
> NTDB_HASH_OFFSET
);
263 ok1(h
.table_size
== 3);
266 /* Should have lock on bucket 0 */
267 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
268 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
269 ok1((ntdb
->flags
& NTDB_NOLOCK
)
270 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
271 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
273 /* YA record: relocation. */
275 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == 0);
276 /* Should have created correct hash. */
277 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
278 /* Should not have located space in chain. */
279 ok1(h
.table
> NTDB_HASH_OFFSET
);
280 ok1(h
.table_size
== 3);
284 /* Should have lock on bucket 0 */
285 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
286 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
287 ok1((ntdb
->flags
& NTDB_NOLOCK
)
288 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
290 new_off
= alloc(ntdb
, key
.dsize
, dbuf
.dsize
,
291 NTDB_USED_MAGIC
, false);
292 ok1(!NTDB_OFF_IS_ERR(new_off2
));
293 off
= new_off
+ sizeof(struct ntdb_used_record
);
294 ok1(!ntdb
->io
->twrite(ntdb
, off
, key
.dptr
, key
.dsize
));
296 ok1(!ntdb
->io
->twrite(ntdb
, off
, dbuf
.dptr
, dbuf
.dsize
));
297 ok1(add_to_hash(ntdb
, &h
, new_off
) == 0);
298 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
300 /* Retrieve it and check. */
301 ok1(find_and_lock(ntdb
, key
, F_WRLCK
, &h
, &rec
, NULL
) == new_off
);
302 /* Should have created correct hash. */
303 ok1(h
.h
== ntdb_hash(ntdb
, key
.dptr
, key
.dsize
));
304 /* Should have appended to chain, bucket 2. */
305 ok1(h
.table
> NTDB_HASH_OFFSET
);
306 ok1(h
.table_size
== 4);
309 /* Should have lock on bucket 0 */
310 ok1((h
.h
& ((1 << ntdb
->hash_bits
)-1)) == 0);
311 ok1((ntdb
->flags
& NTDB_NOLOCK
) || ntdb
->file
->num_lockrecs
== 1);
312 ok1((ntdb
->flags
& NTDB_NOLOCK
)
313 || ntdb
->file
->lockrecs
[0].off
== NTDB_HASH_LOCK_START
);
314 ok1(ntdb_unlock_hash(ntdb
, h
.h
, F_WRLCK
) == 0);
319 ok1(tap_log_messages
== 0);
320 return exit_status();