Import BSDDB 4.7.25 (as of svn r89086)
This commit is contained in:
470
examples_c/csv/DbRecord.c
Normal file
470
examples_c/csv/DbRecord.c
Normal file
@@ -0,0 +1,470 @@
|
||||
/*-
|
||||
* See the file LICENSE for redistribution information.
|
||||
*
|
||||
* Copyright (c) 2005,2008 Oracle. All rights reserved.
|
||||
*
|
||||
* $Id: DbRecord.c 63573 2008-05-23 21:43:21Z trent.nelson $
|
||||
*/
|
||||
|
||||
#include "csv.h"
|
||||
#include "csv_local.h"
|
||||
#include "csv_extern.h"
|
||||
|
||||
static int DbRecord_field(DbRecord *, u_int, void *, datatype);
|
||||
static int DbRecord_search_field(DbField *, char *, OPERATOR);
|
||||
static int DbRecord_search_recno(char *, OPERATOR);
|
||||
|
||||
/*
|
||||
* DbRecord_print --
|
||||
* Display a DbRecord structure.
|
||||
*/
|
||||
void
|
||||
DbRecord_print(DbRecord *recordp, FILE *fp)
|
||||
{
|
||||
DbField *f;
|
||||
void *faddr;
|
||||
|
||||
if (fp == NULL)
|
||||
fp = stdout;
|
||||
|
||||
fprintf(fp, "Record: %lu:\n", (u_long)recordp->recno);
|
||||
for (f = fieldlist; f->name != NULL; ++f) {
|
||||
faddr = (u_int8_t *)recordp + f->offset;
|
||||
fprintf(fp, "\t%s: ", f->name);
|
||||
switch (f->type) {
|
||||
case NOTSET:
|
||||
/* NOTREACHED */
|
||||
abort();
|
||||
break;
|
||||
case DOUBLE:
|
||||
fprintf(fp, "%f\n", *(double *)faddr);
|
||||
break;
|
||||
case STRING:
|
||||
fprintf(fp, "%s\n", *(char **)faddr);
|
||||
break;
|
||||
case UNSIGNED_LONG:
|
||||
fprintf(fp, "%lu\n", *(u_long *)faddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DbRecord_read --
|
||||
* Read a specific record from the database.
|
||||
*/
|
||||
int
|
||||
DbRecord_read(u_long recno_ulong, DbRecord *recordp)
|
||||
{
|
||||
DBT key, data;
|
||||
u_int32_t recno;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* XXX
|
||||
* This code assumes a record number (typed as u_int32_t) is the same
|
||||
* size as an unsigned long, and there's no reason to believe that.
|
||||
*/
|
||||
recno = recno_ulong;
|
||||
|
||||
/*
|
||||
* Retrieve the requested record from the primary database. Have
|
||||
* Berkeley DB allocate memory for us, keeps the DB handle thread
|
||||
* safe.
|
||||
*
|
||||
* We have the Berkeley DB library allocate memory for the record,
|
||||
* which we own and must eventually free. The reason is so we can
|
||||
* have the string fields in the structure point into the actual
|
||||
* record, rather than allocating structure local memory to hold them
|
||||
* and copying them out of the record.
|
||||
*/
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&data, 0, sizeof(data));
|
||||
key.data = &recno;
|
||||
key.size = sizeof(recno);
|
||||
data.flags = DB_DBT_MALLOC;
|
||||
if ((ret = db->get(db, NULL, &key, &data, 0)) != 0)
|
||||
return (ret);
|
||||
|
||||
if ((ret = DbRecord_init(&key, &data, recordp)) != 0)
|
||||
return (ret);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* DbRecord_discard --
|
||||
* Discard a DbRecord structure.
|
||||
*/
|
||||
int
|
||||
DbRecord_discard(DbRecord *recordp)
|
||||
{
|
||||
/* Free the allocated memory. */
|
||||
free(recordp->raw);
|
||||
recordp->raw = NULL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* DbRecord_init --
|
||||
* Fill in a DbRecord from the database key/data pair.
|
||||
*/
|
||||
int
|
||||
DbRecord_init(const DBT *key, const DBT *data, DbRecord *recordp)
|
||||
{
|
||||
DbField *f;
|
||||
u_int32_t skip;
|
||||
void *faddr;
|
||||
|
||||
/* Initialize the structure (get the pre-set index values). */
|
||||
*recordp = DbRecord_base;
|
||||
|
||||
/* Fill in the ID and version. */
|
||||
memcpy(&recordp->recno, key->data, sizeof(u_int32_t));
|
||||
memcpy(&recordp->version,
|
||||
(u_int32_t *)data->data + 1, sizeof(u_int32_t));
|
||||
|
||||
/* Set up the record references. */
|
||||
recordp->raw = data->data;
|
||||
recordp->offset = (u_int32_t *)data->data + 1;
|
||||
skip = (recordp->field_count + 2) * sizeof(u_int32_t);
|
||||
recordp->record = (u_int8_t *)data->data + skip;
|
||||
recordp->record_len = data->size - skip;
|
||||
|
||||
for (f = fieldlist; f->name != NULL; ++f) {
|
||||
faddr = (u_int8_t *)recordp + f->offset;
|
||||
if (DbRecord_field(
|
||||
recordp, f->fieldno, faddr, f->type) != 0)
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* DbRecord_field --
|
||||
* Fill in an individual field of the DbRecord.
|
||||
*/
|
||||
static int
|
||||
DbRecord_field(
|
||||
DbRecord *recordp, u_int field, void *addr, datatype type)
|
||||
{
|
||||
size_t len;
|
||||
char number_buf[20];
|
||||
|
||||
/*
|
||||
* The offset table is 0-based, the field numbers are 1-based.
|
||||
* Correct.
|
||||
*/
|
||||
--field;
|
||||
|
||||
switch (type) {
|
||||
case NOTSET:
|
||||
/* NOTREACHED */
|
||||
abort();
|
||||
break;
|
||||
case STRING:
|
||||
*((u_char **)addr) = recordp->record + recordp->offset[field];
|
||||
recordp->record[recordp->offset[field] +
|
||||
OFFSET_LEN(recordp->offset, field)] = '\0';
|
||||
break;
|
||||
case DOUBLE:
|
||||
case UNSIGNED_LONG:
|
||||
/* This shouldn't be possible -- 2^32 is only 10 digits. */
|
||||
len = OFFSET_LEN(recordp->offset, field);
|
||||
if (len > sizeof(number_buf) - 1) {
|
||||
dbenv->errx(dbenv,
|
||||
"record %lu field %lu: numeric field is %lu bytes and too large to copy",
|
||||
recordp->recno, field, (u_long)len);
|
||||
return (1);
|
||||
}
|
||||
memcpy(number_buf,
|
||||
recordp->record + recordp->offset[field], len);
|
||||
number_buf[len] = '\0';
|
||||
|
||||
if (type == DOUBLE) {
|
||||
if (len == 0)
|
||||
*(double *)addr = 0;
|
||||
else if (strtod_err(number_buf, (double *)addr) != 0)
|
||||
goto fmt_err;
|
||||
} else
|
||||
if (len == 0)
|
||||
*(u_long *)addr = 0;
|
||||
else if (strtoul_err(number_buf, (u_long *)addr) != 0) {
|
||||
fmt_err: dbenv->errx(dbenv,
|
||||
"record %lu: numeric field %u error: %s",
|
||||
recordp->recno, field, number_buf);
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* DbRecord_search_field_name --
|
||||
* Search, looking for a record by field name.
|
||||
*/
|
||||
int
|
||||
DbRecord_search_field_name(char *field, char *value, OPERATOR op)
|
||||
{
|
||||
DbField *f;
|
||||
|
||||
for (f = fieldlist; f->name != NULL; ++f)
|
||||
if (strcasecmp(field, f->name) == 0)
|
||||
return (DbRecord_search_field(f, value, op));
|
||||
|
||||
/* Record numbers aren't handled as fields. */
|
||||
if (strcasecmp(field, "id") == 0)
|
||||
return (DbRecord_search_recno(value, op));
|
||||
|
||||
dbenv->errx(dbenv, "unknown field name: %s", field);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* DbRecord_search_field_number --
|
||||
* Search, looking for a record by field number.
|
||||
*/
|
||||
int
|
||||
DbRecord_search_field_number(u_int32_t fieldno, char *value, OPERATOR op)
|
||||
{
|
||||
DbField *f;
|
||||
|
||||
for (f = fieldlist; f->name != NULL; ++f)
|
||||
if (fieldno == f->fieldno)
|
||||
return (DbRecord_search_field(f, value, op));
|
||||
|
||||
dbenv->errx(dbenv, "field number %lu not configured", (u_long)fieldno);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* DbRecord_search_recno --
|
||||
* Search, looking for a record by record number.
|
||||
*/
|
||||
static int
|
||||
DbRecord_search_recno(char *value, OPERATOR op)
|
||||
{
|
||||
DBC *dbc;
|
||||
DbRecord record;
|
||||
DBT key, data;
|
||||
u_int32_t recno;
|
||||
u_long recno_ulong;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* XXX
|
||||
* This code assumes a record number (typed as u_int32_t) is the same
|
||||
* size as an unsigned long, and there's no reason to believe that.
|
||||
*/
|
||||
if (strtoul_err(value, &recno_ulong) != 0)
|
||||
return (1);
|
||||
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&data, 0, sizeof(data));
|
||||
key.data = &recno;
|
||||
key.size = sizeof(recno);
|
||||
|
||||
if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0)
|
||||
return (ret);
|
||||
|
||||
/*
|
||||
* Retrieve the first record that interests us. The range depends on
|
||||
* the operator:
|
||||
*
|
||||
* ~ error
|
||||
* != beginning to end
|
||||
* < beginning to first match
|
||||
* <= beginning to last match
|
||||
* = first match to last match
|
||||
* > record after last match to end
|
||||
* >= first match to end
|
||||
*/
|
||||
if (op == LT || op == LTEQ || op == NEQ || op == WC || op == NWC)
|
||||
recno = 1;
|
||||
else if (op == WC || op == NWC) {
|
||||
dbenv->errx(dbenv,
|
||||
"wildcard operator only supported for string fields");
|
||||
return (1);
|
||||
} else {
|
||||
recno = recno_ulong;
|
||||
if (op == GT)
|
||||
++recno;
|
||||
}
|
||||
if ((ret = dbc->c_get(dbc, &key, &data, DB_SET)) != 0)
|
||||
goto err;
|
||||
|
||||
for (;;) {
|
||||
if ((ret = DbRecord_init(&key, &data, &record)) != 0)
|
||||
break;
|
||||
if (field_cmp_ulong(&record.recno, &recno_ulong, op))
|
||||
DbRecord_print(&record, NULL);
|
||||
else
|
||||
if (op == LT || op == LTEQ || op == EQ)
|
||||
break;
|
||||
if ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
err: return (ret == DB_NOTFOUND ? 0 : ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* DbRecord_search_field --
|
||||
* Search, looking for a record by field.
|
||||
*/
|
||||
static int
|
||||
DbRecord_search_field(DbField *f, char *value, OPERATOR op)
|
||||
{
|
||||
#ifdef HAVE_WILDCARD_SUPPORT
|
||||
regex_t preq;
|
||||
#endif
|
||||
DBC *dbc;
|
||||
DbRecord record;
|
||||
DBT key, data, pkey;
|
||||
double value_double;
|
||||
u_long value_ulong;
|
||||
u_int32_t cursor_flags;
|
||||
int ret, t_ret;
|
||||
int (*cmp)(void *, void *, OPERATOR);
|
||||
void *faddr, *valuep;
|
||||
|
||||
dbc = NULL;
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&pkey, 0, sizeof(pkey));
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
/*
|
||||
* Initialize the comparison function, crack the value. Wild cards
|
||||
* are always strings, otherwise we follow the field type.
|
||||
*/
|
||||
if (op == WC || op == NWC) {
|
||||
#ifdef HAVE_WILDCARD_SUPPORT
|
||||
if (f->type != STRING) {
|
||||
dbenv->errx(dbenv,
|
||||
"wildcard operator only supported for string fields");
|
||||
return (1);
|
||||
}
|
||||
if (regcomp(&preq, value, 0) != 0) {
|
||||
dbenv->errx(dbenv, "regcomp of pattern failed");
|
||||
return (1);
|
||||
}
|
||||
valuep = &preq;
|
||||
cmp = field_cmp_re;
|
||||
#else
|
||||
dbenv->errx(dbenv,
|
||||
"wildcard operators not supported in this build");
|
||||
return (1);
|
||||
#endif
|
||||
} else
|
||||
switch (f->type) {
|
||||
case DOUBLE:
|
||||
if (strtod_err(value, &value_double) != 0)
|
||||
return (1);
|
||||
cmp = field_cmp_double;
|
||||
valuep = &value_double;
|
||||
key.size = sizeof(double);
|
||||
break;
|
||||
case STRING:
|
||||
valuep = value;
|
||||
cmp = field_cmp_string;
|
||||
key.size = strlen(value);
|
||||
break;
|
||||
case UNSIGNED_LONG:
|
||||
if (strtoul_err(value, &value_ulong) != 0)
|
||||
return (1);
|
||||
cmp = field_cmp_ulong;
|
||||
valuep = &value_ulong;
|
||||
key.size = sizeof(u_long);
|
||||
break;
|
||||
default:
|
||||
case NOTSET:
|
||||
abort();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the first record that interests us. The range depends on
|
||||
* the operator:
|
||||
*
|
||||
* ~ beginning to end
|
||||
* != beginning to end
|
||||
* < beginning to first match
|
||||
* <= beginning to last match
|
||||
* = first match to last match
|
||||
* > record after last match to end
|
||||
* >= first match to end
|
||||
*
|
||||
* If we have a secondary, set a cursor in the secondary, else set the
|
||||
* cursor to the beginning of the primary.
|
||||
*
|
||||
* XXX
|
||||
* If the wildcard string has a leading non-magic character we should
|
||||
* be able to do a range search instead of a full-database search.
|
||||
*
|
||||
* Step through records to the first non-match or to the end of the
|
||||
* database, depending on the operation. If the comparison function
|
||||
* returns success for a key/data pair, print the pair.
|
||||
*/
|
||||
if (f->secondary == NULL || op == NEQ || op == WC || op == NWC) {
|
||||
if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0)
|
||||
goto err;
|
||||
while ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) == 0) {
|
||||
if ((ret = DbRecord_init(&key, &data, &record)) != 0)
|
||||
break;
|
||||
faddr = (u_int8_t *)&record + f->offset;
|
||||
if (cmp(faddr, valuep, op))
|
||||
DbRecord_print(&record, NULL);
|
||||
else
|
||||
if (op == EQ || op == LT || op == LTEQ)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((ret =
|
||||
f->secondary->cursor(f->secondary, NULL, &dbc, 0)) != 0)
|
||||
goto err;
|
||||
key.data = valuep;
|
||||
cursor_flags = op == LT || op == LTEQ ? DB_FIRST : DB_SET_RANGE;
|
||||
if ((ret =
|
||||
dbc->c_pget(dbc, &key, &pkey, &data, cursor_flags)) != 0)
|
||||
goto done;
|
||||
if (op == GT) {
|
||||
while ((ret = dbc->c_pget(
|
||||
dbc, &key, &pkey, &data, DB_NEXT)) == 0) {
|
||||
if ((ret =
|
||||
DbRecord_init(&pkey, &data, &record)) != 0)
|
||||
break;
|
||||
faddr = (u_int8_t *)&record + f->offset;
|
||||
if (cmp(faddr, valuep, op) != 0)
|
||||
break;
|
||||
}
|
||||
if (ret != 0)
|
||||
goto done;
|
||||
}
|
||||
do {
|
||||
if ((ret = DbRecord_init(&pkey, &data, &record)) != 0)
|
||||
break;
|
||||
faddr = (u_int8_t *)&record + f->offset;
|
||||
if (cmp(faddr, valuep, op))
|
||||
DbRecord_print(&record, NULL);
|
||||
else
|
||||
if (op == EQ || op == LT || op == LTEQ)
|
||||
break;
|
||||
} while ((ret =
|
||||
dbc->c_pget(dbc, &key, &pkey, &data, DB_NEXT)) == 0);
|
||||
}
|
||||
|
||||
done: if (ret == DB_NOTFOUND)
|
||||
ret = 0;
|
||||
|
||||
err: if (dbc != NULL && (t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
|
||||
ret = t_ret;
|
||||
|
||||
#ifdef HAVE_WILDCARD_SUPPORT
|
||||
if (op == WC || op == NWC)
|
||||
regfree(&preq);
|
||||
#endif
|
||||
|
||||
return (ret);
|
||||
}
|
||||
Reference in New Issue
Block a user