1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2005,2008 Oracle. All rights reserved. 5 * 6 * $Id: DbRecord.c,v 1.16 2008/01/08 20:58:23 bostic Exp $ 7 */ 8 9#include "csv.h" 10#include "csv_local.h" 11#include "csv_extern.h" 12 13static int DbRecord_field(DbRecord *, u_int, void *, datatype); 14static int DbRecord_search_field(DbField *, char *, OPERATOR); 15static int DbRecord_search_recno(char *, OPERATOR); 16 17/* 18 * DbRecord_print -- 19 * Display a DbRecord structure. 20 */ 21void 22DbRecord_print(DbRecord *recordp, FILE *fp) 23{ 24 DbField *f; 25 void *faddr; 26 27 if (fp == NULL) 28 fp = stdout; 29 30 fprintf(fp, "Record: %lu:\n", (u_long)recordp->recno); 31 for (f = fieldlist; f->name != NULL; ++f) { 32 faddr = (u_int8_t *)recordp + f->offset; 33 fprintf(fp, "\t%s: ", f->name); 34 switch (f->type) { 35 case NOTSET: 36 /* NOTREACHED */ 37 abort(); 38 break; 39 case DOUBLE: 40 fprintf(fp, "%f\n", *(double *)faddr); 41 break; 42 case STRING: 43 fprintf(fp, "%s\n", *(char **)faddr); 44 break; 45 case UNSIGNED_LONG: 46 fprintf(fp, "%lu\n", *(u_long *)faddr); 47 break; 48 } 49 } 50} 51 52/* 53 * DbRecord_read -- 54 * Read a specific record from the database. 55 */ 56int 57DbRecord_read(u_long recno_ulong, DbRecord *recordp) 58{ 59 DBT key, data; 60 u_int32_t recno; 61 int ret; 62 63 /* 64 * XXX 65 * This code assumes a record number (typed as u_int32_t) is the same 66 * size as an unsigned long, and there's no reason to believe that. 67 */ 68 recno = recno_ulong; 69 70 /* 71 * Retrieve the requested record from the primary database. Have 72 * Berkeley DB allocate memory for us, keeps the DB handle thread 73 * safe. 74 * 75 * We have the Berkeley DB library allocate memory for the record, 76 * which we own and must eventually free. The reason is so we can 77 * have the string fields in the structure point into the actual 78 * record, rather than allocating structure local memory to hold them 79 * and copying them out of the record. 80 */ 81 memset(&key, 0, sizeof(key)); 82 memset(&data, 0, sizeof(data)); 83 key.data = &recno; 84 key.size = sizeof(recno); 85 data.flags = DB_DBT_MALLOC; 86 if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) 87 return (ret); 88 89 if ((ret = DbRecord_init(&key, &data, recordp)) != 0) 90 return (ret); 91 92 return (0); 93} 94 95/* 96 * DbRecord_discard -- 97 * Discard a DbRecord structure. 98 */ 99int 100DbRecord_discard(DbRecord *recordp) 101{ 102 /* Free the allocated memory. */ 103 free(recordp->raw); 104 recordp->raw = NULL; 105 106 return (0); 107} 108 109/* 110 * DbRecord_init -- 111 * Fill in a DbRecord from the database key/data pair. 112 */ 113int 114DbRecord_init(const DBT *key, const DBT *data, DbRecord *recordp) 115{ 116 DbField *f; 117 u_int32_t skip; 118 void *faddr; 119 120 /* Initialize the structure (get the pre-set index values). */ 121 *recordp = DbRecord_base; 122 123 /* Fill in the ID and version. */ 124 memcpy(&recordp->recno, key->data, sizeof(u_int32_t)); 125 memcpy(&recordp->version, 126 (u_int32_t *)data->data + 1, sizeof(u_int32_t)); 127 128 /* Set up the record references. */ 129 recordp->raw = data->data; 130 recordp->offset = (u_int32_t *)data->data + 1; 131 skip = (recordp->field_count + 2) * sizeof(u_int32_t); 132 recordp->record = (u_int8_t *)data->data + skip; 133 recordp->record_len = data->size - skip; 134 135 for (f = fieldlist; f->name != NULL; ++f) { 136 faddr = (u_int8_t *)recordp + f->offset; 137 if (DbRecord_field( 138 recordp, f->fieldno, faddr, f->type) != 0) 139 return (1); 140 } 141 return (0); 142} 143 144/* 145 * DbRecord_field -- 146 * Fill in an individual field of the DbRecord. 147 */ 148static int 149DbRecord_field( 150 DbRecord *recordp, u_int field, void *addr, datatype type) 151{ 152 size_t len; 153 char number_buf[20]; 154 155 /* 156 * The offset table is 0-based, the field numbers are 1-based. 157 * Correct. 158 */ 159 --field; 160 161 switch (type) { 162 case NOTSET: 163 /* NOTREACHED */ 164 abort(); 165 break; 166 case STRING: 167 *((u_char **)addr) = recordp->record + recordp->offset[field]; 168 recordp->record[recordp->offset[field] + 169 OFFSET_LEN(recordp->offset, field)] = '\0'; 170 break; 171 case DOUBLE: 172 case UNSIGNED_LONG: 173 /* This shouldn't be possible -- 2^32 is only 10 digits. */ 174 len = OFFSET_LEN(recordp->offset, field); 175 if (len > sizeof(number_buf) - 1) { 176 dbenv->errx(dbenv, 177 "record %lu field %lu: numeric field is %lu bytes and too large to copy", 178 recordp->recno, field, (u_long)len); 179 return (1); 180 } 181 memcpy(number_buf, 182 recordp->record + recordp->offset[field], len); 183 number_buf[len] = '\0'; 184 185 if (type == DOUBLE) { 186 if (len == 0) 187 *(double *)addr = 0; 188 else if (strtod_err(number_buf, (double *)addr) != 0) 189 goto fmt_err; 190 } else 191 if (len == 0) 192 *(u_long *)addr = 0; 193 else if (strtoul_err(number_buf, (u_long *)addr) != 0) { 194fmt_err: dbenv->errx(dbenv, 195 "record %lu: numeric field %u error: %s", 196 recordp->recno, field, number_buf); 197 return (1); 198 } 199 break; 200 } 201 return (0); 202} 203 204/* 205 * DbRecord_search_field_name -- 206 * Search, looking for a record by field name. 207 */ 208int 209DbRecord_search_field_name(char *field, char *value, OPERATOR op) 210{ 211 DbField *f; 212 213 for (f = fieldlist; f->name != NULL; ++f) 214 if (strcasecmp(field, f->name) == 0) 215 return (DbRecord_search_field(f, value, op)); 216 217 /* Record numbers aren't handled as fields. */ 218 if (strcasecmp(field, "id") == 0) 219 return (DbRecord_search_recno(value, op)); 220 221 dbenv->errx(dbenv, "unknown field name: %s", field); 222 return (1); 223} 224 225/* 226 * DbRecord_search_field_number -- 227 * Search, looking for a record by field number. 228 */ 229int 230DbRecord_search_field_number(u_int32_t fieldno, char *value, OPERATOR op) 231{ 232 DbField *f; 233 234 for (f = fieldlist; f->name != NULL; ++f) 235 if (fieldno == f->fieldno) 236 return (DbRecord_search_field(f, value, op)); 237 238 dbenv->errx(dbenv, "field number %lu not configured", (u_long)fieldno); 239 return (1); 240} 241 242/* 243 * DbRecord_search_recno -- 244 * Search, looking for a record by record number. 245 */ 246static int 247DbRecord_search_recno(char *value, OPERATOR op) 248{ 249 DBC *dbc; 250 DbRecord record; 251 DBT key, data; 252 u_int32_t recno; 253 u_long recno_ulong; 254 int ret; 255 256 /* 257 * XXX 258 * This code assumes a record number (typed as u_int32_t) is the same 259 * size as an unsigned long, and there's no reason to believe that. 260 */ 261 if (strtoul_err(value, &recno_ulong) != 0) 262 return (1); 263 264 memset(&key, 0, sizeof(key)); 265 memset(&data, 0, sizeof(data)); 266 key.data = &recno; 267 key.size = sizeof(recno); 268 269 if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0) 270 return (ret); 271 272 /* 273 * Retrieve the first record that interests us. The range depends on 274 * the operator: 275 * 276 * ~ error 277 * != beginning to end 278 * < beginning to first match 279 * <= beginning to last match 280 * = first match to last match 281 * > record after last match to end 282 * >= first match to end 283 */ 284 if (op == LT || op == LTEQ || op == NEQ || op == WC || op == NWC) 285 recno = 1; 286 else if (op == WC || op == NWC) { 287 dbenv->errx(dbenv, 288 "wildcard operator only supported for string fields"); 289 return (1); 290 } else { 291 recno = recno_ulong; 292 if (op == GT) 293 ++recno; 294 } 295 if ((ret = dbc->c_get(dbc, &key, &data, DB_SET)) != 0) 296 goto err; 297 298 for (;;) { 299 if ((ret = DbRecord_init(&key, &data, &record)) != 0) 300 break; 301 if (field_cmp_ulong(&record.recno, &recno_ulong, op)) 302 DbRecord_print(&record, NULL); 303 else 304 if (op == LT || op == LTEQ || op == EQ) 305 break; 306 if ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) != 0) 307 break; 308 } 309 310err: return (ret == DB_NOTFOUND ? 0 : ret); 311} 312 313/* 314 * DbRecord_search_field -- 315 * Search, looking for a record by field. 316 */ 317static int 318DbRecord_search_field(DbField *f, char *value, OPERATOR op) 319{ 320#ifdef HAVE_WILDCARD_SUPPORT 321 regex_t preq; 322#endif 323 DBC *dbc; 324 DbRecord record; 325 DBT key, data, pkey; 326 double value_double; 327 u_long value_ulong; 328 u_int32_t cursor_flags; 329 int ret, t_ret; 330 int (*cmp)(void *, void *, OPERATOR); 331 void *faddr, *valuep; 332 333 dbc = NULL; 334 memset(&key, 0, sizeof(key)); 335 memset(&pkey, 0, sizeof(pkey)); 336 memset(&data, 0, sizeof(data)); 337 338 /* 339 * Initialize the comparison function, crack the value. Wild cards 340 * are always strings, otherwise we follow the field type. 341 */ 342 if (op == WC || op == NWC) { 343#ifdef HAVE_WILDCARD_SUPPORT 344 if (f->type != STRING) { 345 dbenv->errx(dbenv, 346 "wildcard operator only supported for string fields"); 347 return (1); 348 } 349 if (regcomp(&preq, value, 0) != 0) { 350 dbenv->errx(dbenv, "regcomp of pattern failed"); 351 return (1); 352 } 353 valuep = &preq; 354 cmp = field_cmp_re; 355#else 356 dbenv->errx(dbenv, 357 "wildcard operators not supported in this build"); 358 return (1); 359#endif 360 } else 361 switch (f->type) { 362 case DOUBLE: 363 if (strtod_err(value, &value_double) != 0) 364 return (1); 365 cmp = field_cmp_double; 366 valuep = &value_double; 367 key.size = sizeof(double); 368 break; 369 case STRING: 370 valuep = value; 371 cmp = field_cmp_string; 372 key.size = strlen(value); 373 break; 374 case UNSIGNED_LONG: 375 if (strtoul_err(value, &value_ulong) != 0) 376 return (1); 377 cmp = field_cmp_ulong; 378 valuep = &value_ulong; 379 key.size = sizeof(u_long); 380 break; 381 default: 382 case NOTSET: 383 abort(); 384 /* NOTREACHED */ 385 } 386 387 /* 388 * Retrieve the first record that interests us. The range depends on 389 * the operator: 390 * 391 * ~ beginning to end 392 * != beginning to end 393 * < beginning to first match 394 * <= beginning to last match 395 * = first match to last match 396 * > record after last match to end 397 * >= first match to end 398 * 399 * If we have a secondary, set a cursor in the secondary, else set the 400 * cursor to the beginning of the primary. 401 * 402 * XXX 403 * If the wildcard string has a leading non-magic character we should 404 * be able to do a range search instead of a full-database search. 405 * 406 * Step through records to the first non-match or to the end of the 407 * database, depending on the operation. If the comparison function 408 * returns success for a key/data pair, print the pair. 409 */ 410 if (f->secondary == NULL || op == NEQ || op == WC || op == NWC) { 411 if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0) 412 goto err; 413 while ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) == 0) { 414 if ((ret = DbRecord_init(&key, &data, &record)) != 0) 415 break; 416 faddr = (u_int8_t *)&record + f->offset; 417 if (cmp(faddr, valuep, op)) 418 DbRecord_print(&record, NULL); 419 else 420 if (op == EQ || op == LT || op == LTEQ) 421 break; 422 } 423 } else { 424 if ((ret = 425 f->secondary->cursor(f->secondary, NULL, &dbc, 0)) != 0) 426 goto err; 427 key.data = valuep; 428 cursor_flags = op == LT || op == LTEQ ? DB_FIRST : DB_SET_RANGE; 429 if ((ret = 430 dbc->c_pget(dbc, &key, &pkey, &data, cursor_flags)) != 0) 431 goto done; 432 if (op == GT) { 433 while ((ret = dbc->c_pget( 434 dbc, &key, &pkey, &data, DB_NEXT)) == 0) { 435 if ((ret = 436 DbRecord_init(&pkey, &data, &record)) != 0) 437 break; 438 faddr = (u_int8_t *)&record + f->offset; 439 if (cmp(faddr, valuep, op) != 0) 440 break; 441 } 442 if (ret != 0) 443 goto done; 444 } 445 do { 446 if ((ret = DbRecord_init(&pkey, &data, &record)) != 0) 447 break; 448 faddr = (u_int8_t *)&record + f->offset; 449 if (cmp(faddr, valuep, op)) 450 DbRecord_print(&record, NULL); 451 else 452 if (op == EQ || op == LT || op == LTEQ) 453 break; 454 } while ((ret = 455 dbc->c_pget(dbc, &key, &pkey, &data, DB_NEXT)) == 0); 456 } 457 458done: if (ret == DB_NOTFOUND) 459 ret = 0; 460 461err: if (dbc != NULL && (t_ret = dbc->c_close(dbc)) != 0 && ret == 0) 462 ret = t_ret; 463 464#ifdef HAVE_WILDCARD_SUPPORT 465 if (op == WC || op == NWC) 466 regfree(&preq); 467#endif 468 469 return (ret); 470} 471