1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /* use sequental numbers printed to strings
7 * to store lots and lots of entries in the
8 * database.
9 *
10 * Start with 100 entries, put them and then
11 * read them out. Then delete the first
12 * half and verify that all of the first half
13 * is gone and then verify that the second
14 * half is still there.
15 * Then add the first half back and verify
16 * again. Then delete the middle third
17 * and verify again.
18 * Then increase the size by 1000 and do
19 * the whole add delete thing again.
20 *
21 * The data for each object is the number string translated
22 * to hex and replicated a random number of times. The
23 * number of times that the data is replicated is the first
24 * int32 in the data.
25 */
26
27 #include <stdio.h>
28
29 #include <stdlib.h>
30 #ifdef STDC_HEADERS
31 #include <stdarg.h>
32 #else
33 #include <varargs.h>
34 #endif
35
36 #ifdef HAVE_MEMORY_H
37 #include <memory.h>
38 #endif
39 #include <string.h>
40 #include <assert.h>
41 #include "mcom_db.h"
42
43 DB *database = 0;
44 int MsgPriority = 5;
45
46 #if defined(_WINDOWS) && !defined(WIN32)
47 #define int32 long
48 #define uint32 unsigned long
49 #else
50 #define int32 int
51 #define uint32 unsigned int
52 #endif
53
54 typedef enum {
55 USE_LARGE_KEY,
56 USE_SMALL_KEY
57 } key_type_enum;
58
59 #define TraceMe(priority, msg) \
60 do { \
61 if (priority <= MsgPriority) { \
62 ReportStatus msg; \
63 } \
64 } while (0)
65
66 int
ReportStatus(char * string,...)67 ReportStatus(char *string, ...)
68 {
69 va_list args;
70
71 #ifdef STDC_HEADERS
72 va_start(args, string);
73 #else
74 va_start(args);
75 #endif
76 vfprintf(stderr, string, args);
77 va_end(args);
78
79 fprintf(stderr, "\n");
80
81 return (0);
82 }
83
84 int
ReportError(char * string,...)85 ReportError(char *string, ...)
86 {
87 va_list args;
88
89 #ifdef STDC_HEADERS
90 va_start(args, string);
91 #else
92 va_start(args);
93 #endif
94 fprintf(stderr, "\n ");
95 vfprintf(stderr, string, args);
96 fprintf(stderr, "\n");
97 va_end(args);
98
99 return (0);
100 }
101
102 DBT *
MakeLargeKey(int32 num)103 MakeLargeKey(int32 num)
104 {
105 int32 low_bits;
106 static DBT rv;
107 static char *string_rv = 0;
108 int rep_char;
109 size_t size;
110
111 if (string_rv)
112 free(string_rv);
113
114 /* generate a really large text key derived from
115 * an int32
116 */
117 low_bits = (num % 10000) + 1;
118
119 /* get the repeat char from the low 26 */
120 rep_char = (char)((low_bits % 26) + 'a');
121
122 /* malloc a string low_bits wide */
123 size = low_bits * sizeof(char);
124 string_rv = (char *)malloc(size);
125
126 memset(string_rv, rep_char, size);
127
128 rv.data = string_rv;
129 rv.size = size;
130
131 return (&rv);
132 }
133
134 DBT *
MakeSmallKey(int32 num)135 MakeSmallKey(int32 num)
136 {
137 static DBT rv;
138 static char data_string[64];
139
140 rv.data = data_string;
141
142 sprintf(data_string, "%ld", (long)num);
143 rv.size = strlen(data_string);
144
145 return (&rv);
146 }
147
148 DBT *
GenKey(int32 num,key_type_enum key_type)149 GenKey(int32 num, key_type_enum key_type)
150 {
151 DBT *key;
152
153 switch (key_type) {
154 case USE_LARGE_KEY:
155 key = MakeLargeKey(num);
156 break;
157 case USE_SMALL_KEY:
158 key = MakeSmallKey(num);
159 break;
160 default:
161 abort();
162 break;
163 }
164
165 return (key);
166 }
167
168 int
SeqDatabase()169 SeqDatabase()
170 {
171 int status;
172 DBT key, data;
173
174 ReportStatus("SEQuencing through database...");
175
176 /* seq through the whole database */
177 if (!(status = (*database->seq)(database, &key, &data, R_FIRST))) {
178 while (!(status = (database->seq)(database, &key, &data, R_NEXT)))
179 ; /* null body */
180 }
181
182 if (status < 0)
183 ReportError("Error seq'ing database");
184
185 return (status);
186 }
187
188 int
VerifyData(DBT * data,int32 num,key_type_enum key_type)189 VerifyData(DBT *data, int32 num, key_type_enum key_type)
190 {
191 int32 count, compare_num;
192 size_t size;
193 int32 *int32_array;
194
195 /* The first int32 is count
196 * The other n entries should
197 * all equal num
198 */
199 if (data->size < sizeof(int32)) {
200 ReportError("Data size corrupted");
201 return -1;
202 }
203
204 memcpy(&count, data->data, sizeof(int32));
205
206 size = sizeof(int32) * (count + 1);
207
208 if (size != data->size) {
209 ReportError("Data size corrupted");
210 return -1;
211 }
212
213 int32_array = (int32 *)data->data;
214
215 for (; count > 0; count--) {
216 memcpy(&compare_num, &int32_array[count], sizeof(int32));
217
218 if (compare_num != num) {
219 ReportError("Data corrupted");
220 return -1;
221 }
222 }
223
224 return (0);
225 }
226
227 /* verify that a range of number strings exist
228 * or don't exist. And that the data is valid
229 */
230 #define SHOULD_EXIST 1
231 #define SHOULD_NOT_EXIST 0
232 int
VerifyRange(int32 low,int32 high,int32 should_exist,key_type_enum key_type)233 VerifyRange(int32 low, int32 high, int32 should_exist, key_type_enum key_type)
234 {
235 DBT *key, data;
236 int32 num;
237 int status;
238
239 TraceMe(1, ("Verifying: %ld to %ld, using %s keys",
240 low, high, key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
241
242 for (num = low; num <= high; num++) {
243
244 key = GenKey(num, key_type);
245
246 status = (*database->get)(database, key, &data, 0);
247
248 if (status == 0) {
249 /* got the item */
250 if (!should_exist) {
251 ReportError("Item exists but shouldn't: %ld", num);
252 } else {
253 /* else verify the data */
254 VerifyData(&data, num, key_type);
255 }
256 } else if (status > 0) {
257 /* item not found */
258 if (should_exist) {
259 ReportError("Item not found but should be: %ld", num);
260 }
261 } else {
262 /* database error */
263 ReportError("Database error");
264 return (-1);
265 }
266 }
267
268 TraceMe(1, ("Correctly verified: %ld to %ld", low, high));
269
270 return (0);
271 }
272
273 DBT *
GenData(int32 num)274 GenData(int32 num)
275 {
276 int32 n;
277 static DBT *data = 0;
278 int32 *int32_array;
279 size_t size;
280
281 if (!data) {
282 data = (DBT *)malloc(sizeof(DBT));
283 data->size = 0;
284 data->data = 0;
285 } else if (data->data) {
286 free(data->data);
287 }
288
289 n = rand();
290
291 n = n % 512; /* bound to a 2K size */
292
293 size = sizeof(int32) * (n + 1);
294 int32_array = (int32 *)malloc(size);
295
296 memcpy(&int32_array[0], &n, sizeof(int32));
297
298 for (; n > 0; n--) {
299 memcpy(&int32_array[n], &num, sizeof(int32));
300 }
301
302 data->data = (void *)int32_array;
303 data->size = size;
304
305 return (data);
306 }
307
308 #define ADD_RANGE 1
309 #define DELETE_RANGE 2
310
311 int
AddOrDelRange(int32 low,int32 high,int action,key_type_enum key_type)312 AddOrDelRange(int32 low, int32 high, int action, key_type_enum key_type)
313 {
314 DBT *key, *data;
315 #if 0 /* only do this if your really analy checking the puts */
316 DBT tmp_data;
317 #endif
318 int32 num;
319 int status;
320
321 if (action != ADD_RANGE && action != DELETE_RANGE)
322 assert(0);
323
324 if (action == ADD_RANGE) {
325 TraceMe(1, ("Adding: %ld to %ld: %s keys", low, high,
326 key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
327 } else {
328 TraceMe(1, ("Deleting: %ld to %ld: %s keys", low, high,
329 key_type == USE_SMALL_KEY ? "SMALL" : "LARGE"));
330 }
331
332 for (num = low; num <= high; num++) {
333
334 key = GenKey(num, key_type);
335
336 if (action == ADD_RANGE) {
337 data = GenData(num);
338 status = (*database->put)(database, key, data, 0);
339 } else {
340 status = (*database->del)(database, key, 0);
341 }
342
343 if (status < 0) {
344 ReportError("Database error %s item: %ld",
345 action == ADD_RANGE ? "ADDING" : "DELETING",
346 num);
347 } else if (status > 0) {
348 ReportError("Could not %s item: %ld",
349 action == ADD_RANGE ? "ADD" : "DELETE",
350 num);
351 } else if (action == ADD_RANGE) {
352 #define SYNC_EVERY_TIME
353 #ifdef SYNC_EVERY_TIME
354 status = (*database->sync)(database, 0);
355 if (status != 0)
356 ReportError("Database error syncing after add");
357 #endif
358
359 #if 0 /* only do this if your really analy checking the puts */
360
361 /* make sure we can still get it
362 */
363 status = (*database->get)(database, key, &tmp_data, 0);
364
365 if(status != 0)
366 {
367 ReportError("Database error checking item just added: %d",
368 num);
369 }
370 else
371 {
372 /* now verify that none of the ones we already
373 * put in have disappeared
374 */
375 VerifyRange(low, num, SHOULD_EXIST, key_type);
376 }
377 #endif
378 }
379 }
380
381 if (action == ADD_RANGE) {
382 TraceMe(1, ("Successfully added: %ld to %ld", low, high));
383 } else {
384 TraceMe(1, ("Successfully deleted: %ld to %ld", low, high));
385 }
386
387 return (0);
388 }
389
390 int
TestRange(int32 low,int32 range,key_type_enum key_type)391 TestRange(int32 low, int32 range, key_type_enum key_type)
392 {
393 int status;
394 int32 low_of_range1, high_of_range1;
395 int32 low_of_range2, high_of_range2;
396 int32 low_of_range3, high_of_range3;
397
398 status = AddOrDelRange(low, low + range, ADD_RANGE, key_type);
399 status = VerifyRange(low, low + range, SHOULD_EXIST, key_type);
400
401 TraceMe(1, ("Finished with sub test 1"));
402
403 SeqDatabase();
404
405 low_of_range1 = low;
406 high_of_range1 = low + (range / 2);
407 low_of_range2 = high_of_range1 + 1;
408 high_of_range2 = low + range;
409 status = AddOrDelRange(low_of_range1, high_of_range1, DELETE_RANGE, key_type);
410 status = VerifyRange(low_of_range1, high_of_range1, SHOULD_NOT_EXIST, key_type);
411 status = VerifyRange(low_of_range2, low_of_range2, SHOULD_EXIST, key_type);
412
413 TraceMe(1, ("Finished with sub test 2"));
414
415 SeqDatabase();
416
417 status = AddOrDelRange(low_of_range1, high_of_range1, ADD_RANGE, key_type);
418 /* the whole thing should exist now */
419 status = VerifyRange(low, low + range, SHOULD_EXIST, key_type);
420
421 TraceMe(1, ("Finished with sub test 3"));
422
423 SeqDatabase();
424
425 status = AddOrDelRange(low_of_range2, high_of_range2, DELETE_RANGE, key_type);
426 status = VerifyRange(low_of_range1, high_of_range1, SHOULD_EXIST, key_type);
427 status = VerifyRange(low_of_range2, high_of_range2, SHOULD_NOT_EXIST, key_type);
428
429 TraceMe(1, ("Finished with sub test 4"));
430
431 SeqDatabase();
432
433 status = AddOrDelRange(low_of_range2, high_of_range2, ADD_RANGE, key_type);
434 /* the whole thing should exist now */
435 status = VerifyRange(low, low + range, SHOULD_EXIST, key_type);
436
437 TraceMe(1, ("Finished with sub test 5"));
438
439 SeqDatabase();
440
441 low_of_range1 = low;
442 high_of_range1 = low + (range / 3);
443 low_of_range2 = high_of_range1 + 1;
444 high_of_range2 = high_of_range1 + (range / 3);
445 low_of_range3 = high_of_range2 + 1;
446 high_of_range3 = low + range;
447 /* delete range 2 */
448 status = AddOrDelRange(low_of_range2, high_of_range2, DELETE_RANGE, key_type);
449 status = VerifyRange(low_of_range1, high_of_range1, SHOULD_EXIST, key_type);
450 status = VerifyRange(low_of_range2, low_of_range2, SHOULD_NOT_EXIST, key_type);
451 status = VerifyRange(low_of_range3, low_of_range2, SHOULD_EXIST, key_type);
452
453 TraceMe(1, ("Finished with sub test 6"));
454
455 SeqDatabase();
456
457 status = AddOrDelRange(low_of_range2, high_of_range2, ADD_RANGE, key_type);
458 /* the whole thing should exist now */
459 status = VerifyRange(low, low + range, SHOULD_EXIST, key_type);
460
461 TraceMe(1, ("Finished with sub test 7"));
462
463 return (0);
464 }
465
466 #define START_RANGE 109876
467 int
main(int argc,char ** argv)468 main(int argc, char **argv)
469 {
470 int32 i, j = 0;
471 int quick_exit = 0;
472 int large_keys = 0;
473 HASHINFO hash_info = {
474 16 * 1024,
475 0,
476 0,
477 0,
478 0,
479 0
480 };
481
482 if (argc > 1) {
483 while (argc > 1) {
484 if (!strcmp(argv[argc - 1], "-quick"))
485 quick_exit = 1;
486 else if (!strcmp(argv[argc - 1], "-large")) {
487 large_keys = 1;
488 }
489 argc--;
490 }
491 }
492
493 database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, &hash_info);
494
495 if (!database) {
496 ReportError("Could not open database");
497 #ifdef unix
498 perror("");
499 #endif
500 exit(1);
501 }
502
503 if (quick_exit) {
504 if (large_keys)
505 TestRange(START_RANGE, 200, USE_LARGE_KEY);
506 else
507 TestRange(START_RANGE, 200, USE_SMALL_KEY);
508
509 (*database->sync)(database, 0);
510 (*database->close)(database);
511 exit(0);
512 }
513
514 for (i = 100; i < 10000000; i += 200) {
515 if (1 || j) {
516 TestRange(START_RANGE, i, USE_LARGE_KEY);
517 j = 0;
518 } else {
519 TestRange(START_RANGE, i, USE_SMALL_KEY);
520 j = 1;
521 }
522
523 if (1 == rand() % 3) {
524 (*database->sync)(database, 0);
525 }
526
527 if (1 == rand() % 3) {
528 /* close and reopen */
529 (*database->close)(database);
530 database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, 0);
531 if (!database) {
532 ReportError("Could not reopen database");
533 #ifdef unix
534 perror("");
535 #endif
536 exit(1);
537 }
538 } else {
539 /* reopen database without closeing the other */
540 database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, 0);
541 if (!database) {
542 ReportError("Could not reopen database "
543 "after not closing the other");
544 #ifdef unix
545 perror("");
546 #endif
547 exit(1);
548 }
549 }
550 }
551
552 return (0);
553 }
554