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