Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

DbRecord.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: DbRecord.c,v 1.10 2005/10/14 12:50:37 bostic Exp $
00008  */
00009 
00010 #include "csv.h"
00011 #include "csv_local.h"
00012 #include "csv_extern.h"
00013 
00014 static int      DbRecord_field(DbRecord *, u_int, void *, datatype);
00015 static int      DbRecord_search_field(DbField *, char *, OPERATOR);
00016 static int      DbRecord_search_recno(char *, OPERATOR);
00017 
00018 /*
00019  * DbRecord_print --
00020  *      Display a DbRecord structure.
00021  */
00022 void
00023 DbRecord_print(DbRecord *recordp, FILE *fp)
00024 {
00025         DbField *f;
00026         void *faddr;
00027 
00028         if (fp == NULL)
00029                 fp = stdout;
00030 
00031         fprintf(fp, "Record: %lu:\n", (u_long)recordp->recno);
00032         for (f = fieldlist; f->name != NULL; ++f) {
00033                 faddr = (u_int8_t *)recordp + f->offset;
00034                 fprintf(fp, "\t%s: ", f->name);
00035                 switch (f->type) {
00036                 case NOTSET:
00037                         /* NOTREACHED */
00038                         abort();
00039                         break;
00040                 case DOUBLE:
00041                         fprintf(fp, "%f\n", *(double *)faddr);
00042                         break;
00043                 case STRING:
00044                         fprintf(fp, "%s\n", *(char **)faddr);
00045                         break;
00046                 case ULONG:
00047                         fprintf(fp, "%lu\n", *(u_long *)faddr);
00048                         break;
00049                 }
00050         }
00051 }
00052 
00053 /*
00054  * DbRecord_read --
00055  *      Read a specific record from the database.
00056  */
00057 int
00058 DbRecord_read(u_long recno_ulong, DbRecord *recordp)
00059 {
00060         DBT key, data;
00061         u_int32_t recno;
00062         int ret;
00063 
00064         /*
00065          * XXX
00066          * This code assumes a record number (typed as u_int32_t) is the same
00067          * size as an unsigned long, and there's no reason to believe that.
00068          */
00069         recno = recno_ulong;
00070 
00071         /*
00072          * Retrieve the requested record from the primary database.  Have
00073          * Berkeley DB allocate memory for us, keeps the DB handle thread
00074          * safe.
00075          *
00076          * We have the Berkeley DB library allocate memory for the record,
00077          * which we own and must eventually free.  The reason is so we can
00078          * have the string fields in the structure point into the actual
00079          * record, rather than allocating structure local memory to hold them
00080          * and copying them out of the record.
00081          */
00082         memset(&key, 0, sizeof(key));
00083         memset(&data, 0, sizeof(data));
00084         key.data = &recno;
00085         key.size = sizeof(recno);
00086         data.flags = DB_DBT_MALLOC;
00087         if ((ret = db->get(db, NULL, &key, &data, 0)) != 0)
00088                 return (ret);
00089 
00090         if ((ret = DbRecord_init(&key, &data, recordp)) != 0)
00091                 return (ret);
00092 
00093         return (0);
00094 }
00095 
00096 /*
00097  * DbRecord_discard --
00098  *      Discard a DbRecord structure.
00099  */
00100 int
00101 DbRecord_discard(DbRecord *recordp)
00102 {
00103         /* Free the allocated memory. */
00104         free(recordp->raw);
00105         recordp->raw = NULL;
00106 
00107         return (0);
00108 }
00109 
00110 /*
00111  * DbRecord_init --
00112  *      Fill in a DbRecord from the database key/data pair.
00113  */
00114 int
00115 DbRecord_init(const DBT *key, const DBT *data, DbRecord *recordp)
00116 {
00117         DbField *f;
00118         u_int32_t skip;
00119         void *faddr;
00120 
00121         /* Initialize the structure (get the pre-set index values). */
00122         *recordp = DbRecord_base;
00123 
00124         /* Fill in the ID and version. */
00125         memcpy(&recordp->recno, key->data, sizeof(u_int32_t));
00126         memcpy(&recordp->version,
00127             (u_int32_t *)data->data + 1, sizeof(u_int32_t));
00128 
00129         /* Set up the record references. */
00130         recordp->raw = data->data;
00131         recordp->offset = (u_int32_t *)data->data + 1;
00132         skip = (recordp->field_count + 2) * sizeof(u_int32_t);
00133         recordp->record = (u_int8_t *)data->data + skip;
00134         recordp->record_len = data->size - skip;
00135 
00136         for (f = fieldlist; f->name != NULL; ++f) {
00137                 faddr = (u_int8_t *)recordp + f->offset;
00138                 if (DbRecord_field(
00139                     recordp, f->fieldno, faddr, f->type) != 0)
00140                         return (1);
00141         }
00142         return (0);
00143 }
00144 
00145 /*
00146  * DbRecord_field --
00147  *      Fill in an individual field of the DbRecord.
00148  */
00149 static int
00150 DbRecord_field(
00151     DbRecord *recordp, u_int field, void *addr, datatype type)
00152 {
00153         size_t len;
00154         char number_buf[20];
00155 
00156         /*
00157          * The offset table is 0-based, the field numbers are 1-based.
00158          * Correct.
00159          */
00160         --field;
00161 
00162         switch (type) {
00163         case NOTSET:
00164                 /* NOTREACHED */
00165                 abort();
00166                 break;
00167         case STRING:
00168                 *((u_char **)addr) = recordp->record + recordp->offset[field];
00169                 recordp->record[recordp->offset[field] +
00170                     OFFSET_LEN(recordp->offset, field)] = '\0';
00171                 break;
00172         case DOUBLE:
00173         case ULONG:
00174                 /* This shouldn't be possible -- 2^32 is only 10 digits. */
00175                 len = OFFSET_LEN(recordp->offset, field);
00176                 if (len > sizeof(number_buf) - 1) {
00177                         dbenv->errx(dbenv,
00178     "record %lu field %lu: numeric field is %lu bytes and too large to copy",
00179                             recordp->recno, field, (u_long)len);
00180                         return (1);
00181                 }
00182                 memcpy(number_buf,
00183                     recordp->record + recordp->offset[field], len);
00184                 number_buf[len] = '\0';
00185 
00186                 if (type == DOUBLE) {
00187                         if (len == 0)
00188                                 *(double *)addr = 0;
00189                         else if (strtod_err(number_buf, (double *)addr) != 0)
00190                                 goto fmt_err;
00191                 } else
00192                         if (len == 0)
00193                                 *(u_long *)addr = 0;
00194                         else if (strtoul_err(number_buf, (u_long *)addr) != 0) {
00195 fmt_err:                        dbenv->errx(dbenv,
00196                                     "record %lu: numeric field %u error: %s",
00197                                     recordp->recno, field, number_buf);
00198                                 return (1);
00199                         }
00200                 break;
00201         }
00202         return (0);
00203 }
00204 
00205 /*
00206  * DbRecord_search_field_name --
00207  *      Search, looking for a record by field name.
00208  */
00209 int
00210 DbRecord_search_field_name(char *field, char *value, OPERATOR op)
00211 {
00212         DbField *f;
00213 
00214         for (f = fieldlist; f->name != NULL; ++f)
00215                 if (strcasecmp(field, f->name) == 0)
00216                         return (DbRecord_search_field(f, value, op));
00217 
00218         /* Record numbers aren't handled as fields. */
00219         if (strcasecmp(field, "id") == 0)
00220                 return (DbRecord_search_recno(value, op));
00221 
00222         dbenv->errx(dbenv, "unknown field name: %s", field);
00223         return (1);
00224 }
00225 
00226 /*
00227  * DbRecord_search_field_number --
00228  *      Search, looking for a record by field number.
00229  */
00230 int
00231 DbRecord_search_field_number(u_int32_t fieldno, char *value, OPERATOR op)
00232 {
00233         DbField *f;
00234 
00235         for (f = fieldlist; f->name != NULL; ++f)
00236                 if (fieldno == f->fieldno)
00237                         return (DbRecord_search_field(f, value, op));
00238 
00239         dbenv->errx(dbenv, "field number %lu not configured", (u_long)fieldno);
00240         return (1);
00241 }
00242 
00243 /*
00244  * DbRecord_search_recno --
00245  *      Search, looking for a record by record number.
00246  */
00247 static int
00248 DbRecord_search_recno(char *value, OPERATOR op)
00249 {
00250         DBC *dbc;
00251         DbRecord record;
00252         DBT key, data;
00253         u_int32_t recno;
00254         u_long recno_ulong;
00255         int ret;
00256 
00257         /*
00258          * XXX
00259          * This code assumes a record number (typed as u_int32_t) is the same
00260          * size as an unsigned long, and there's no reason to believe that.
00261          */
00262         if (strtoul_err(value, &recno_ulong) != 0)
00263                 return (1);
00264 
00265         memset(&key, 0, sizeof(key));
00266         memset(&data, 0, sizeof(data));
00267         key.data = &recno;
00268         key.size = sizeof(recno);
00269 
00270         if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0)
00271                 return (ret);
00272 
00273         /*
00274          * Retrieve the first record that interests us.  The range depends on
00275          * the operator:
00276          *
00277          *      ~               error
00278          *      !=              beginning to end
00279          *      <               beginning to first match
00280          *      <=              beginning to last match
00281          *      =               first match to last match
00282          *      >               record after last match to end
00283          *      >=              first match to end
00284          */
00285         if (op == LT || op == LTEQ || op == NEQ || op == WC || op == NWC)
00286                 recno = 1;
00287         else if (op == WC || op == NWC) {
00288                 dbenv->errx(dbenv,
00289                     "wildcard operator only supported for string fields");
00290                 return (1);
00291         } else {
00292                 recno = recno_ulong;
00293                 if (op == GT)
00294                         ++recno;
00295         }
00296         if ((ret = dbc->c_get(dbc, &key, &data, DB_SET)) != 0)
00297                 goto err;
00298 
00299         for (;;) {
00300                 if ((ret = DbRecord_init(&key, &data, &record)) != 0)
00301                         break;
00302                 if (field_cmp_ulong(&record.recno, &recno_ulong, op))
00303                         DbRecord_print(&record, NULL);
00304                 else
00305                         if (op == LT || op == LTEQ || op == EQ)
00306                                 break;
00307                 if ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) != 0)
00308                         break;
00309         }
00310 
00311 err:    return (ret == DB_NOTFOUND ? 0 : ret);
00312 }
00313 
00314 /*
00315  * DbRecord_search_field --
00316  *      Search, looking for a record by field.
00317  */
00318 static int
00319 DbRecord_search_field(DbField *f, char *value, OPERATOR op)
00320 {
00321 #ifdef HAVE_WILDCARD_SUPPORT
00322         regex_t preq;
00323 #endif
00324         DBC *dbc;
00325         DbRecord record;
00326         DBT key, data, pkey;
00327         double value_double;
00328         u_long value_ulong;
00329         u_int32_t cursor_flags;
00330         int ret, t_ret;
00331         int (*cmp)(void *, void *, OPERATOR);
00332         void *faddr, *valuep;
00333 
00334         dbc = NULL;
00335         memset(&key, 0, sizeof(key));
00336         memset(&pkey, 0, sizeof(pkey));
00337         memset(&data, 0, sizeof(data));
00338 
00339         /*
00340          * Initialize the comparison function, crack the value.  Wild cards
00341          * are always strings, otherwise we follow the field type.
00342          */
00343         if (op == WC || op == NWC) {
00344 #ifdef HAVE_WILDCARD_SUPPORT
00345                 if (f->type != STRING) {
00346                         dbenv->errx(dbenv,
00347                     "wildcard operator only supported for string fields");
00348                         return (1);
00349                 }
00350                 if (regcomp(&preq, value, 0) != 0) {
00351                         dbenv->errx(dbenv, "regcomp of pattern failed");
00352                         return (1);
00353                 }
00354                 valuep = &preq;
00355                 cmp = field_cmp_re;
00356 #else
00357                 dbenv->errx(dbenv,
00358                     "wildcard operators not supported in this build");
00359                 return (1);
00360 #endif
00361         } else
00362                 switch (f->type) {
00363                 case DOUBLE:
00364                         if (strtod_err(value, &value_double) != 0)
00365                                 return (1);
00366                         cmp = field_cmp_double;
00367                         valuep = &value_double;
00368                         key.size = sizeof(double);
00369                         break;
00370                 case STRING:
00371                         valuep = value;
00372                         cmp = field_cmp_string;
00373                         key.size = strlen(value);
00374                         break;
00375                 case ULONG:
00376                         if (strtoul_err(value, &value_ulong) != 0)
00377                                 return (1);
00378                         cmp = field_cmp_ulong;
00379                         valuep = &value_ulong;
00380                         key.size = sizeof(u_long);
00381                         break;
00382                 default:
00383                 case NOTSET:
00384                         abort();
00385                         /* NOTREACHED */
00386                 }
00387 
00388         /*
00389          * Retrieve the first record that interests us.  The range depends on
00390          * the operator:
00391          *
00392          *      ~               beginning to end
00393          *      !=              beginning to end
00394          *      <               beginning to first match
00395          *      <=              beginning to last match
00396          *      =               first match to last match
00397          *      >               record after last match to end
00398          *      >=              first match to end
00399          *
00400          * If we have a secondary, set a cursor in the secondary, else set the
00401          * cursor to the beginning of the primary.
00402          *
00403          * XXX
00404          * If the wildcard string has a leading non-magic character we should
00405          * be able to do a range search instead of a full-database search.
00406          *
00407          * Step through records to the first non-match or to the end of the
00408          * database, depending on the operation.  If the comparison function
00409          * returns success for a key/data pair, print the pair.
00410          */
00411         if (f->secondary == NULL || op == NEQ || op == WC || op == NWC) {
00412                 if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0)
00413                         goto err;
00414                 while ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) == 0) {
00415                         if ((ret = DbRecord_init(&key, &data, &record)) != 0)
00416                                 break;
00417                         faddr = (u_int8_t *)&record + f->offset;
00418                         if (cmp(faddr, valuep, op))
00419                                 DbRecord_print(&record, NULL);
00420                         else
00421                                 if (op == EQ || op == LT || op == LTEQ)
00422                                         break;
00423                 }
00424         } else {
00425                 if ((ret =
00426                     f->secondary->cursor(f->secondary, NULL, &dbc, 0)) != 0)
00427                         goto err;
00428                 key.data = valuep;
00429                 cursor_flags = op == LT || op == LTEQ ? DB_FIRST : DB_SET_RANGE;
00430                 if ((ret =
00431                     dbc->c_pget(dbc, &key, &pkey, &data, cursor_flags)) != 0)
00432                         goto done;
00433                 if (op == GT) {
00434                         while ((ret = dbc->c_pget(
00435                             dbc, &key, &pkey, &data, DB_NEXT)) == 0) {
00436                                 if ((ret =
00437                                     DbRecord_init(&pkey, &data, &record)) != 0)
00438                                         break;
00439                                 faddr = (u_int8_t *)&record + f->offset;
00440                                 if (cmp(faddr, valuep, op) != 0)
00441                                         break;
00442                         }
00443                         if (ret != 0)
00444                                 goto done;
00445                 }
00446                 do {
00447                         if ((ret = DbRecord_init(&pkey, &data, &record)) != 0)
00448                                 break;
00449                         faddr = (u_int8_t *)&record + f->offset;
00450                         if (cmp(faddr, valuep, op))
00451                                 DbRecord_print(&record, NULL);
00452                         else
00453                                 if (op == EQ || op == LT || op == LTEQ)
00454                                         break;
00455                 } while ((ret =
00456                     dbc->c_pget(dbc, &key, &pkey, &data, DB_NEXT)) == 0);
00457         }
00458 
00459 done:   if (ret == DB_NOTFOUND)
00460                 ret = 0;
00461 
00462 err:    if (dbc != NULL && (t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
00463                 ret = t_ret;
00464 
00465 #ifdef HAVE_WILDCARD_SUPPORT
00466         if (op == WC || op == NWC)
00467                 regfree(&preq);
00468 #endif
00469 
00470         return (ret);
00471 }

Generated on Sun Dec 25 12:14:25 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2