blob: 91bba4965c4ebfef06e642d50bab1d6b0f8e302c [file] [log] [blame]
/* -*- 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);
}