int secondary_callback(DB *db_arg, const DBT *key, const DBT *data, DBT *result) { DbField *f; DbRecord record; void *faddr, *addr; /* Populate the field. */ if (DbRecord_init(key, data, &record) != 0) return (-1); f = db_arg->app_private; faddr = (u_int8_t *)&record + f->offset; /* * If necessary, copy the field into separate memory. * Set up the result DBT. */ switch (f->type) { case STRING: result->data = *(char **)faddr; result->size = (u_int32_t)strlen(*(char **)faddr) + 1; break; case DOUBLE: if ((addr = malloc(sizeof(double))) == NULL) return (-1); result->data = addr; result->size = sizeof(double); result->flags = DB_DBT_APPMALLOC; memcpy(addr, faddr, sizeof(double)); break; case UNSIGNED_LONG: if ((addr = malloc(sizeof(u_long))) == NULL) return (-1); result->data = addr; result->size = sizeof(u_long); result->flags = DB_DBT_APPMALLOC; memcpy(addr, faddr, sizeof(u_long)); break; default: case NOTSET: abort(); /* NOTREACHED */ } return (0); }
/* * 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_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 = (u_int32_t)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); }
/* * 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); }