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