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