michael@0: /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* use sequental numbers printed to strings michael@0: * to store lots and lots of entries in the michael@0: * database. michael@0: * michael@0: * Start with 100 entries, put them and then michael@0: * read them out. Then delete the first michael@0: * half and verify that all of the first half michael@0: * is gone and then verify that the second michael@0: * half is still there. michael@0: * Then add the first half back and verify michael@0: * again. Then delete the middle third michael@0: * and verify again. michael@0: * Then increase the size by 1000 and do michael@0: * the whole add delete thing again. michael@0: * michael@0: * The data for each object is the number string translated michael@0: * to hex and replicated a random number of times. The michael@0: * number of times that the data is replicated is the first michael@0: * int32 in the data. michael@0: */ michael@0: michael@0: #include michael@0: michael@0: #include michael@0: #ifdef STDC_HEADERS michael@0: #include michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef HAVE_MEMORY_H michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include "mcom_db.h" michael@0: michael@0: DB *database=0; michael@0: int MsgPriority=5; michael@0: michael@0: #if defined(_WINDOWS) && !defined(WIN32) michael@0: #define int32 long michael@0: #define uint32 unsigned long michael@0: #else michael@0: #define int32 int michael@0: #define uint32 unsigned int michael@0: #endif michael@0: michael@0: typedef enum { michael@0: USE_LARGE_KEY, michael@0: USE_SMALL_KEY michael@0: } key_type_enum; michael@0: michael@0: #define TraceMe(priority, msg) \ michael@0: do { \ michael@0: if(priority <= MsgPriority) \ michael@0: { \ michael@0: ReportStatus msg; \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: int michael@0: ReportStatus(char *string, ...) michael@0: { michael@0: va_list args; michael@0: michael@0: #ifdef STDC_HEADERS michael@0: va_start(args, string); michael@0: #else michael@0: va_start(args); michael@0: #endif michael@0: vfprintf(stderr, string, args); michael@0: va_end(args); michael@0: michael@0: fprintf (stderr, "\n"); michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: int michael@0: ReportError(char *string, ...) michael@0: { michael@0: va_list args; michael@0: michael@0: #ifdef STDC_HEADERS michael@0: va_start(args, string); michael@0: #else michael@0: va_start(args); michael@0: #endif michael@0: fprintf (stderr, "\n "); michael@0: vfprintf(stderr, string, args); michael@0: fprintf (stderr, "\n"); michael@0: va_end(args); michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: DBT * MakeLargeKey(int32 num) michael@0: { michael@0: int32 low_bits; michael@0: static DBT rv; michael@0: static char *string_rv=0; michael@0: int rep_char; michael@0: size_t size; michael@0: michael@0: if(string_rv) michael@0: free(string_rv); michael@0: michael@0: /* generate a really large text key derived from michael@0: * an int32 michael@0: */ michael@0: low_bits = (num % 10000) + 1; michael@0: michael@0: /* get the repeat char from the low 26 */ michael@0: rep_char = (char) ((low_bits % 26) + 'a'); michael@0: michael@0: /* malloc a string low_bits wide */ michael@0: size = low_bits*sizeof(char); michael@0: string_rv = (char *)malloc(size); michael@0: michael@0: memset(string_rv, rep_char, size); michael@0: michael@0: rv.data = string_rv; michael@0: rv.size = size; michael@0: michael@0: return(&rv); michael@0: } michael@0: michael@0: DBT * MakeSmallKey(int32 num) michael@0: { michael@0: static DBT rv; michael@0: static char data_string[64]; michael@0: michael@0: rv.data = data_string; michael@0: michael@0: sprintf(data_string, "%ld", (long)num); michael@0: rv.size = strlen(data_string); michael@0: michael@0: return(&rv); michael@0: michael@0: } michael@0: michael@0: DBT * GenKey(int32 num, key_type_enum key_type) michael@0: { michael@0: DBT *key; michael@0: michael@0: switch(key_type) michael@0: { michael@0: case USE_LARGE_KEY: michael@0: key = MakeLargeKey(num); michael@0: break; michael@0: case USE_SMALL_KEY: michael@0: key = MakeSmallKey(num); michael@0: break; michael@0: default: michael@0: abort(); michael@0: break; michael@0: } michael@0: michael@0: return(key); michael@0: } michael@0: michael@0: int michael@0: SeqDatabase() michael@0: { michael@0: int status; michael@0: DBT key, data; michael@0: michael@0: ReportStatus("SEQuencing through database..."); michael@0: michael@0: /* seq through the whole database */ michael@0: if(!(status = (*database->seq)(database, &key, &data, R_FIRST))) michael@0: { michael@0: while(!(status = (database->seq) (database, &key, &data, R_NEXT))) michael@0: ; /* null body */ michael@0: } michael@0: michael@0: if(status < 0) michael@0: ReportError("Error seq'ing database"); michael@0: michael@0: return(status); michael@0: } michael@0: michael@0: int michael@0: VerifyData(DBT *data, int32 num, key_type_enum key_type) michael@0: { michael@0: int32 count, compare_num; michael@0: size_t size; michael@0: int32 *int32_array; michael@0: michael@0: /* The first int32 is count michael@0: * The other n entries should michael@0: * all equal num michael@0: */ michael@0: if(data->size < sizeof(int32)) michael@0: { michael@0: ReportError("Data size corrupted"); michael@0: return -1; michael@0: } michael@0: michael@0: memcpy(&count, data->data, sizeof(int32)); michael@0: michael@0: size = sizeof(int32)*(count+1); michael@0: michael@0: if(size != data->size) michael@0: { michael@0: ReportError("Data size corrupted"); michael@0: return -1; michael@0: } michael@0: michael@0: int32_array = (int32*)data->data; michael@0: michael@0: for(;count > 0; count--) michael@0: { michael@0: memcpy(&compare_num, &int32_array[count], sizeof(int32)); michael@0: michael@0: if(compare_num != num) michael@0: { michael@0: ReportError("Data corrupted"); michael@0: return -1; michael@0: } michael@0: } michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: michael@0: /* verify that a range of number strings exist michael@0: * or don't exist. And that the data is valid michael@0: */ michael@0: #define SHOULD_EXIST 1 michael@0: #define SHOULD_NOT_EXIST 0 michael@0: int michael@0: VerifyRange(int32 low, int32 high, int32 should_exist, key_type_enum key_type) michael@0: { michael@0: DBT *key, data; michael@0: int32 num; michael@0: int status; michael@0: michael@0: TraceMe(1, ("Verifying: %ld to %ld, using %s keys", michael@0: low, high, key_type == USE_SMALL_KEY ? "SMALL" : "LARGE")); michael@0: michael@0: for(num = low; num <= high; num++) michael@0: { michael@0: michael@0: key = GenKey(num, key_type); michael@0: michael@0: status = (*database->get)(database, key, &data, 0); michael@0: michael@0: if(status == 0) michael@0: { michael@0: /* got the item */ michael@0: if(!should_exist) michael@0: { michael@0: ReportError("Item exists but shouldn't: %ld", num); michael@0: } michael@0: else michael@0: { michael@0: /* else verify the data */ michael@0: VerifyData(&data, num, key_type); michael@0: } michael@0: } michael@0: else if(status > 0) michael@0: { michael@0: /* item not found */ michael@0: if(should_exist) michael@0: { michael@0: ReportError("Item not found but should be: %ld", num); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: /* database error */ michael@0: ReportError("Database error"); michael@0: return(-1); michael@0: } michael@0: michael@0: } michael@0: michael@0: TraceMe(1, ("Correctly verified: %ld to %ld", low, high)); michael@0: michael@0: return(0); michael@0: michael@0: } michael@0: michael@0: DBT * michael@0: GenData(int32 num) michael@0: { michael@0: int32 n; michael@0: static DBT *data=0; michael@0: int32 *int32_array; michael@0: size_t size; michael@0: michael@0: if(!data) michael@0: { michael@0: data = (DBT*)malloc(sizeof(DBT)); michael@0: data->size = 0; michael@0: data->data = 0; michael@0: } michael@0: else if(data->data) michael@0: { michael@0: free(data->data); michael@0: } michael@0: michael@0: n = rand(); michael@0: michael@0: n = n % 512; /* bound to a 2K size */ michael@0: michael@0: michael@0: size = sizeof(int32)*(n+1); michael@0: int32_array = (int32 *) malloc(size); michael@0: michael@0: memcpy(&int32_array[0], &n, sizeof(int32)); michael@0: michael@0: for(; n > 0; n--) michael@0: { michael@0: memcpy(&int32_array[n], &num, sizeof(int32)); michael@0: } michael@0: michael@0: data->data = (void*)int32_array; michael@0: data->size = size; michael@0: michael@0: return(data); michael@0: } michael@0: michael@0: #define ADD_RANGE 1 michael@0: #define DELETE_RANGE 2 michael@0: michael@0: int michael@0: AddOrDelRange(int32 low, int32 high, int action, key_type_enum key_type) michael@0: { michael@0: DBT *key, *data; michael@0: #if 0 /* only do this if your really analy checking the puts */ michael@0: DBT tmp_data; michael@0: #endif michael@0: int32 num; michael@0: int status; michael@0: michael@0: if(action != ADD_RANGE && action != DELETE_RANGE) michael@0: assert(0); michael@0: michael@0: if(action == ADD_RANGE) michael@0: { michael@0: TraceMe(1, ("Adding: %ld to %ld: %s keys", low, high, michael@0: key_type == USE_SMALL_KEY ? "SMALL" : "LARGE")); michael@0: } michael@0: else michael@0: { michael@0: TraceMe(1, ("Deleting: %ld to %ld: %s keys", low, high, michael@0: key_type == USE_SMALL_KEY ? "SMALL" : "LARGE")); michael@0: } michael@0: michael@0: for(num = low; num <= high; num++) michael@0: { michael@0: michael@0: key = GenKey(num, key_type); michael@0: michael@0: if(action == ADD_RANGE) michael@0: { michael@0: data = GenData(num); michael@0: status = (*database->put)(database, key, data, 0); michael@0: } michael@0: else michael@0: { michael@0: status = (*database->del)(database, key, 0); michael@0: } michael@0: michael@0: if(status < 0) michael@0: { michael@0: ReportError("Database error %s item: %ld", michael@0: action == ADD_RANGE ? "ADDING" : "DELETING", michael@0: num); michael@0: } michael@0: else if(status > 0) michael@0: { michael@0: ReportError("Could not %s item: %ld", michael@0: action == ADD_RANGE ? "ADD" : "DELETE", michael@0: num); michael@0: } michael@0: else if(action == ADD_RANGE) michael@0: { michael@0: #define SYNC_EVERY_TIME michael@0: #ifdef SYNC_EVERY_TIME michael@0: status = (*database->sync)(database, 0); michael@0: if(status != 0) michael@0: ReportError("Database error syncing after add"); michael@0: #endif michael@0: michael@0: #if 0 /* only do this if your really analy checking the puts */ michael@0: michael@0: /* make sure we can still get it michael@0: */ michael@0: status = (*database->get)(database, key, &tmp_data, 0); michael@0: michael@0: if(status != 0) michael@0: { michael@0: ReportError("Database error checking item just added: %d", michael@0: num); michael@0: } michael@0: else michael@0: { michael@0: /* now verify that none of the ones we already michael@0: * put in have disappeared michael@0: */ michael@0: VerifyRange(low, num, SHOULD_EXIST, key_type); michael@0: } michael@0: #endif michael@0: michael@0: } michael@0: } michael@0: michael@0: michael@0: if(action == ADD_RANGE) michael@0: { michael@0: TraceMe(1, ("Successfully added: %ld to %ld", low, high)); michael@0: } michael@0: else michael@0: { michael@0: TraceMe(1, ("Successfully deleted: %ld to %ld", low, high)); michael@0: } michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: int michael@0: TestRange(int32 low, int32 range, key_type_enum key_type) michael@0: { michael@0: int status; int32 low_of_range1, high_of_range1; int32 low_of_range2, high_of_range2; michael@0: int32 low_of_range3, high_of_range3; michael@0: michael@0: status = AddOrDelRange(low, low+range, ADD_RANGE, key_type); michael@0: status = VerifyRange(low, low+range, SHOULD_EXIST, key_type); michael@0: michael@0: TraceMe(1, ("Finished with sub test 1")); michael@0: michael@0: SeqDatabase(); michael@0: michael@0: low_of_range1 = low; michael@0: high_of_range1 = low+(range/2); michael@0: low_of_range2 = high_of_range1+1; michael@0: high_of_range2 = low+range; michael@0: status = AddOrDelRange(low_of_range1, high_of_range1, DELETE_RANGE, key_type); michael@0: status = VerifyRange(low_of_range1, high_of_range1, SHOULD_NOT_EXIST, key_type); michael@0: status = VerifyRange(low_of_range2, low_of_range2, SHOULD_EXIST, key_type); michael@0: michael@0: TraceMe(1, ("Finished with sub test 2")); michael@0: michael@0: SeqDatabase(); michael@0: michael@0: status = AddOrDelRange(low_of_range1, high_of_range1, ADD_RANGE, key_type); michael@0: /* the whole thing should exist now */ michael@0: status = VerifyRange(low, low+range, SHOULD_EXIST, key_type); michael@0: michael@0: TraceMe(1, ("Finished with sub test 3")); michael@0: michael@0: SeqDatabase(); michael@0: michael@0: status = AddOrDelRange(low_of_range2, high_of_range2, DELETE_RANGE, key_type); michael@0: status = VerifyRange(low_of_range1, high_of_range1, SHOULD_EXIST, key_type); michael@0: status = VerifyRange(low_of_range2, high_of_range2, SHOULD_NOT_EXIST, key_type); michael@0: michael@0: TraceMe(1, ("Finished with sub test 4")); michael@0: michael@0: SeqDatabase(); michael@0: michael@0: status = AddOrDelRange(low_of_range2, high_of_range2, ADD_RANGE, key_type); michael@0: /* the whole thing should exist now */ michael@0: status = VerifyRange(low, low+range, SHOULD_EXIST, key_type); michael@0: michael@0: TraceMe(1, ("Finished with sub test 5")); michael@0: michael@0: SeqDatabase(); michael@0: michael@0: low_of_range1 = low; michael@0: high_of_range1 = low+(range/3); michael@0: low_of_range2 = high_of_range1+1; michael@0: high_of_range2 = high_of_range1+(range/3); michael@0: low_of_range3 = high_of_range2+1; michael@0: high_of_range3 = low+range; michael@0: /* delete range 2 */ michael@0: status = AddOrDelRange(low_of_range2, high_of_range2, DELETE_RANGE, key_type); michael@0: status = VerifyRange(low_of_range1, high_of_range1, SHOULD_EXIST, key_type); michael@0: status = VerifyRange(low_of_range2, low_of_range2, SHOULD_NOT_EXIST, key_type); michael@0: status = VerifyRange(low_of_range3, low_of_range2, SHOULD_EXIST, key_type); michael@0: michael@0: TraceMe(1, ("Finished with sub test 6")); michael@0: michael@0: SeqDatabase(); michael@0: michael@0: status = AddOrDelRange(low_of_range2, high_of_range2, ADD_RANGE, key_type); michael@0: /* the whole thing should exist now */ michael@0: status = VerifyRange(low, low+range, SHOULD_EXIST, key_type); michael@0: michael@0: TraceMe(1, ("Finished with sub test 7")); michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: #define START_RANGE 109876 michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: int32 i, j=0; michael@0: int quick_exit = 0; michael@0: int large_keys = 0; michael@0: HASHINFO hash_info = { michael@0: 16*1024, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: 0}; michael@0: michael@0: michael@0: if(argc > 1) michael@0: { michael@0: while(argc > 1) michael@0: { michael@0: if(!strcmp(argv[argc-1], "-quick")) michael@0: quick_exit = 1; michael@0: else if(!strcmp(argv[argc-1], "-large")) michael@0: { michael@0: large_keys = 1; michael@0: } michael@0: argc--; michael@0: } michael@0: } michael@0: michael@0: database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, &hash_info); michael@0: michael@0: if(!database) michael@0: { michael@0: ReportError("Could not open database"); michael@0: #ifdef unix michael@0: perror(""); michael@0: #endif michael@0: exit(1); michael@0: } michael@0: michael@0: if(quick_exit) michael@0: { michael@0: if(large_keys) michael@0: TestRange(START_RANGE, 200, USE_LARGE_KEY); michael@0: else michael@0: TestRange(START_RANGE, 200, USE_SMALL_KEY); michael@0: michael@0: (*database->sync)(database, 0); michael@0: (*database->close)(database); michael@0: exit(0); michael@0: } michael@0: michael@0: for(i=100; i < 10000000; i+=200) michael@0: { michael@0: if(1 || j) michael@0: { michael@0: TestRange(START_RANGE, i, USE_LARGE_KEY); michael@0: j = 0; michael@0: } michael@0: else michael@0: { michael@0: TestRange(START_RANGE, i, USE_SMALL_KEY); michael@0: j = 1; michael@0: } michael@0: michael@0: if(1 == rand() % 3) michael@0: { michael@0: (*database->sync)(database, 0); michael@0: } michael@0: michael@0: if(1 == rand() % 3) michael@0: { michael@0: /* close and reopen */ michael@0: (*database->close)(database); michael@0: database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, 0); michael@0: if(!database) michael@0: { michael@0: ReportError("Could not reopen database"); michael@0: #ifdef unix michael@0: perror(""); michael@0: #endif michael@0: exit(1); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: /* reopen database without closeing the other */ michael@0: database = dbopen("test.db", O_RDWR | O_CREAT, 0644, DB_HASH, 0); michael@0: if(!database) michael@0: { michael@0: ReportError("Could not reopen database " michael@0: "after not closing the other"); michael@0: #ifdef unix michael@0: perror(""); michael@0: #endif michael@0: exit(1); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return(0); michael@0: }