/*
NAME
gtconv - test GDBM database conversion to extended format.
SYNOPSIS
gtconv [-v]
DESCRIPTION
When converting a traditional GDBM database to extended (numsync)
format, the size of the master av_table shrinks. Consequently,
if it is full or nearly full, the entries near its end that
don't fit into the new size are returned to the per-bucket
available pools using _gdbm_free (see _gdbm_convert_to_numsync).
This test program verifies that all main av_table entries are
preserved during format upgrade.
Operation:
1) Create a database with the minimal possible block size, to ensure
the mimimal size of the av_table array
2) Set the GDBM_SETCENTFREE option, so all released entries are
returned to the main av_table.
3) Populate the database with a sufficient number of entries.
4) Keep deleting entries until main av_table becomes full.
5) Save a copy of all avail_elems (both master and per-block),
sorted by av_adr.
6) Convert the database to the GDBM_NUMSYNC format.
7) Get a copy of all avail_elems similar to (5)
8) Compare arrays obtained in 5 and 7.
The array obtained in step 7 is normally one entry longer than the
one from step 5. The comparison in 8 ignores such extra entries.
OPTIONS
-v Verbosely print what's being done.
Repeated twice, dumps listings of av_table arrays received in
steps 5 and 7.
EXIT CODE
0 success
1 failure
2 usage error
77 unable to fill the av_table in step 4.
LICENSE
This file is part of GDBM test suite.
Copyright (C) 2021 Free Software Foundation, Inc.
GDBM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GDBM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GDBM. If not, see .
*/
#include "autoconf.h"
#include "gdbmdefs.h"
#include
#include
#include
char dbname[] = "a.db";
#define DATASIZE (4*IGNORE_SIZE)
static int
avail_counter (avail_block *blk, off_t off, void *closure)
{
int *np = closure;
*np += blk->count;
return 0;
}
static int
avail_saver (avail_block *blk, off_t off, void *closure)
{
avail_block *ab = closure;
memcpy (ab->av_table + ab->count, blk->av_table,
blk->count * sizeof (blk->av_table[0]));
ab->count += blk->count;
return 0;
}
static int
av_table_cmp (const void *a, const void *b)
{
const avail_elem *ea = a;
const avail_elem *eb = b;
if (ea->av_adr < eb->av_adr)
return -1;
else if (ea->av_adr > eb->av_adr)
return 1;
return 0;
}
static avail_block *
collect_avail (GDBM_FILE dbf)
{
int av_count;
avail_block *ab;
/* Compute the number of entries in avail block */
av_count = 0;
gdbm_avail_traverse (dbf, avail_counter, &av_count);
/* Allocate temporary storage */
ab = malloc (sizeof (ab[0]) + (av_count - 1) * sizeof (ab->av_table[0]));
assert (ab != NULL);
/* Save the avail table */
ab->size = av_count;
ab->count = 0;
gdbm_avail_traverse (dbf, avail_saver, ab);
/* Sort it */
qsort (ab->av_table, ab->count, sizeof (ab->av_table[0]), av_table_cmp);
return ab;
}
static void
dump_avail (avail_block *ab, char const *title, FILE *fp)
{
int i;
unsigned long total = 0;
fprintf (fp, "%s\n", title);
for (i = 0; i < ab->count; i++)
{
total += ab->av_table[i].av_size;
fprintf (fp, "% 4d %6lu\n", ab->av_table[i].av_size,
(unsigned long) ab->av_table[i].av_adr);
}
fprintf (fp, "total = %lu\n", total);
}
int
main (int argc, char **argv)
{
int avcount;
GDBM_FILE dbf;
datum key, content;
char data[DATASIZE];
int nkeys;
int *keys;
int i, n;
avail_block *av_saved, *av_new;
int verbose = 0;
int rc;
while ((i = getopt (argc, argv, "v")) != EOF)
{
switch (i)
{
case 'v':
verbose++;
break;
default:
return 2;
}
}
/* Make sure we create new database */
unlink (dbname);
/* Create the database */
if (verbose)
printf ("creating database\n");
dbf = gdbm_open (dbname, GDBM_MIN_BLOCK_SIZE, GDBM_NEWDB, 0644, NULL);
if (!dbf)
{
fprintf (stderr, "gdbm_open: %s\n", gdbm_strerror (gdbm_errno));
return 1;
}
int t = 1;
if (gdbm_setopt (dbf, GDBM_SETCENTFREE, &t, sizeof (t)) == -1)
{
fprintf (stderr, "gdbm_setopt: %s\n", gdbm_strerror (gdbm_errno));
return 1;
}
avcount = dbf->avail->size;
if (verbose)
printf ("main av_table capacity: %d\n", avcount);
/* Initialize keys */
nkeys = 2*avcount;
keys = calloc (nkeys, sizeof (keys));
assert (keys != NULL);
for (i = 0; i < nkeys; i++)
{
keys[i] = i+1;
}
/* Initialize content */
for (i = 0; i < DATASIZE; i++)
data[i] = i+1;
content.dsize = DATASIZE;
content.dptr = data;
/* Populate the database */
if (verbose)
printf ("populating database (%d keys)\n", nkeys);
key.dsize = sizeof (keys[0]);
for (i = 0; i < nkeys; i++)
{
key.dptr = (char*) &keys[i];
if (gdbm_store (dbf, key, content, 0) != 0)
{
fprintf (stderr, "%d: item not inserted: %s\n",
i, gdbm_db_strerror (dbf));
gdbm_close (dbf);
return 1;
}
}
/* Delete all keys */
if (verbose)
printf ("deleting keys\n");
i = 0;
while (dbf->avail->count < dbf->avail->size)
{
if (i == nkeys)
{
if (verbose)
printf ("failed to fill av_table\n");
gdbm_close (dbf);
return 77;
}
key.dptr = (char*) &keys[i];
if (gdbm_delete (dbf, key))
{
fprintf (stderr, "%d: gdbm_delete: %s\n",
i, gdbm_db_strerror (dbf));
gdbm_close (dbf);
return 1;
}
i++;
}
if (verbose)
printf ("main av_table elements: %d\n", dbf->avail->count);
av_saved = collect_avail (dbf);
if (verbose)
printf ("total number of avail_elem entries used: %d\n", av_saved->count);
if (verbose > 1)
dump_avail (av_saved, "av_saved", stdout);
/* Upgrade the database */
if (verbose)
printf ("converting database\n");
if (gdbm_convert (dbf, GDBM_NUMSYNC))
{
fprintf (stderr, "gdbm_convert: %s\n", gdbm_db_strerror (dbf));
gdbm_close (dbf);
return 1;
}
if (verbose)
printf ("main av_table elements: %d / %d\n", dbf->avail->count, dbf->avail->size);
av_new = collect_avail (dbf);
if (verbose)
printf ("total number of avail_elem entries used: %d\n", av_new->count);
if (verbose > 1)
dump_avail (av_new, "av_new", stdout);
n = (av_saved->count < av_new->count) ? av_saved->count : av_new->count;
rc = 0;
for (i = 0; i < n; i++)
{
if (!(av_saved->av_table[i].av_adr == av_new->av_table[i].av_adr &&
av_saved->av_table[i].av_size == av_new->av_table[i].av_size))
{
fprintf (stderr, "element %d differs\n", i);
rc = 1;
break;
}
}
if (rc)
{
dump_avail (av_saved, "av_saved", stderr);
dump_avail (av_new, "av_new", stderr);
}
gdbm_close (dbf);
return rc;
}