1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2005,2008 Oracle. All rights reserved. 5 * 6 * $Id: db.c,v 1.18 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 compare_uint32(DB *, const DBT *, const DBT *); 14 15/* 16 * csv_env_init -- 17 * Initialize the database environment. 18 */ 19int 20csv_env_open(const char *home, int is_rdonly) 21{ 22 int ret; 23 24 dbenv = NULL; 25 db = NULL; 26 27 /* Create a database environment handle. */ 28 if ((ret = db_env_create(&dbenv, 0)) != 0) { 29 fprintf(stderr, 30 "%s: db_env_create: %s\n", progname, db_strerror(ret)); 31 return (1); 32 } 33 34 /* 35 * Configure Berkeley DB error reporting to stderr, with our program 36 * name as the prefix. 37 */ 38 dbenv->set_errfile(dbenv, stderr); 39 dbenv->set_errpfx(dbenv, progname); 40 41 /* 42 * The default Berkeley DB cache size is fairly small; configure a 43 * 1MB cache for now. This value will require tuning in the future. 44 */ 45 if ((ret = dbenv->set_cachesize(dbenv, 0, 1048576, 1)) != 0) { 46 dbenv->err(dbenv, ret, "DB_ENV->set_cachesize"); 47 return (1); 48 } 49 50 /* 51 * We may be working with an existing environment -- try and join it. 52 * If that fails, create a new database environment; for now, we only 53 * need a cache, no logging, locking, or transactions. 54 */ 55 if ((ret = dbenv->open(dbenv, home, 56 DB_JOINENV | DB_USE_ENVIRON, 0)) != 0 && 57 (ret = dbenv->open(dbenv, home, 58 DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0)) != 0) { 59 dbenv->err(dbenv, ret, "DB_ENV->open"); 60 return (1); 61 } 62 63 /* Create the primary database handle. */ 64 if ((ret = db_create(&db, dbenv, 0)) != 0) { 65 dbenv->err(dbenv, ret, "db_create"); 66 return (1); 67 } 68 69 /* 70 * Records may be relatively large -- use a large page size. 71 */ 72 if ((ret = db->set_pagesize(db, 32 * 1024)) != 0) { 73 dbenv->err(dbenv, ret, "DB->set_pagesize"); 74 return (1); 75 } 76 77 /* 78 * The primary database uses an integer as its key; on little-endian 79 * machines, integers sort badly using the default Berkeley DB sort 80 * function (which is lexicographic). Specify a comparison function 81 * for the database. 82 */ 83 if ((ret = db->set_bt_compare(db, compare_uint32)) != 0) { 84 dbenv->err(dbenv, ret, "DB->set_bt_compare"); 85 return (1); 86 } 87 88 /* Open the primary database. */ 89 if ((ret = db->open(db, NULL, 90 "primary", NULL, DB_BTREE, is_rdonly ? 0 : DB_CREATE, 0)) != 0) { 91 dbenv->err(dbenv, ret, "DB->open: primary"); 92 return (1); 93 } 94 95 /* Open the secondaries. */ 96 if ((ret = csv_secondary_open()) != 0) 97 return (1); 98 99 return (0); 100} 101 102/* 103 * csv_env_close -- 104 * Discard the database environment. 105 */ 106int 107csv_env_close() 108{ 109 int ret, t_ret; 110 111 ret = 0; 112 113 /* Close the secondaries. */ 114 ret = csv_secondary_close(); 115 116 /* Close the primary handle. */ 117 if (db != NULL && (t_ret = db->close(db, 0)) != 0) { 118 dbenv->err(dbenv, ret, "DB->close"); 119 if (ret == 0) 120 ret = t_ret; 121 } 122 if ((t_ret = dbenv->close(dbenv, 0)) != 0) { 123 fprintf(stderr, 124 "%s: DB_ENV->close: %s\n", progname, db_strerror(ret)); 125 if (ret == 0) 126 ret = t_ret; 127 } 128 129 return (ret); 130} 131 132/* 133 * csv_secondary_open -- 134 * Open any secondary indices. 135 */ 136int 137csv_secondary_open() 138{ 139 DB *sdb; 140 DbField *f; 141 int ret, (*fcmp)(DB *, const DBT *, const DBT *); 142 143 /* 144 * Create secondary database handles. 145 */ 146 for (f = fieldlist; f->name != NULL; ++f) { 147 if (f->indx == 0) 148 continue; 149 150 if ((ret = db_create(&sdb, dbenv, 0)) != 0) { 151 dbenv->err(dbenv, ret, "db_create"); 152 return (1); 153 } 154 sdb->app_private = f; 155 156 /* Keys are small, use a relatively small page size. */ 157 if ((ret = sdb->set_pagesize(sdb, 8 * 1024)) != 0) { 158 dbenv->err(dbenv, ret, "DB->set_pagesize"); 159 return (1); 160 } 161 162 /* 163 * Sort the database based on the underlying type. Skip 164 * strings, Berkeley DB defaults to lexicographic sort. 165 */ 166 switch (f->type) { 167 case DOUBLE: 168 fcmp = compare_double; 169 break; 170 case UNSIGNED_LONG: 171 fcmp = compare_ulong; 172 break; 173 case NOTSET: 174 case STRING: 175 default: 176 fcmp = NULL; 177 break; 178 } 179 if (fcmp != NULL && 180 (ret = sdb->set_bt_compare(sdb, fcmp)) != 0) { 181 dbenv->err(dbenv, ret, "DB->set_bt_compare"); 182 return (1); 183 } 184 185 /* Always configure secondaries for sorted duplicates. */ 186 if ((ret = sdb->set_flags(sdb, DB_DUPSORT)) != 0) { 187 dbenv->err(dbenv, ret, "DB->set_flags"); 188 return (1); 189 } 190 if ((ret = sdb->set_dup_compare(sdb, compare_ulong)) != 0) { 191 dbenv->err(dbenv, ret, "DB->set_dup_compare"); 192 return (1); 193 } 194 195 if ((ret = sdb->open( 196 sdb, NULL, f->name, NULL, DB_BTREE, DB_CREATE, 0)) != 0) { 197 dbenv->err(dbenv, ret, "DB->open: %s", f->name); 198 return (1); 199 } 200 if ((ret = sdb->associate( 201 db, NULL, sdb, secondary_callback, DB_CREATE)) != 0) { 202 dbenv->err(dbenv, ret, "DB->set_associate"); 203 return (1); 204 } 205 f->secondary = sdb; 206 } 207 208 return (0); 209} 210 211/* 212 * csv_secondary_close -- 213 * Close any secondary indices. 214 */ 215int 216csv_secondary_close() 217{ 218 DbField *f; 219 int ret, t_ret; 220 221 ret = 0; 222 for (f = fieldlist; f->name != NULL; ++f) 223 if (f->secondary != NULL && (t_ret = 224 f->secondary->close(f->secondary, 0)) != 0 && ret == 0) 225 ret = t_ret; 226 227 return (ret); 228} 229 230/* 231 * compare_uint32 -- 232 * Compare two keys. 233 */ 234static int 235compare_uint32(DB *db_arg, const DBT *a_arg, const DBT *b_arg) 236{ 237 u_int32_t a, b; 238 239 db_arg = db_arg; /* Quiet compiler. */ 240 241 memcpy(&a, a_arg->data, sizeof(u_int32_t)); 242 memcpy(&b, b_arg->data, sizeof(u_int32_t)); 243 return (a > b ? 1 : ((a < b) ? -1 : 0)); 244} 245