1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1998 5 * Sleepycat Software. All rights reserved. 6 */ 7 8 #include "config.h" 9 10 #ifndef lint 11 static const char sccsid[] = "@(#)db_am.c 10.15 (Sleepycat) 12/30/98"; 12 #endif /* not lint */ 13 14 #ifndef NO_SYSTEM_INCLUDES 15 #include <sys/types.h> 16 17 #include <errno.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #endif 21 22 #include "db_int.h" 23 #include "shqueue.h" 24 #include "db_page.h" 25 #include "db_shash.h" 26 #include "mp.h" 27 #include "btree.h" 28 #include "hash.h" 29 #include "db_am.h" 30 #include "db_ext.h" 31 32 static int __db_c_close __P((DBC *)); 33 static int __db_cursor __P((DB *, DB_TXN *, DBC **, u_int32_t)); 34 static int __db_fd __P((DB *, int *)); 35 static int __db_get __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); 36 static int __db_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); 37 38 /* 39 * __db_init_wrapper -- 40 * Wrapper layer to implement generic DB functions. 41 * 42 * PUBLIC: int __db_init_wrapper __P((DB *)); 43 */ 44 int 45 __db_init_wrapper(dbp) 46 DB *dbp; 47 { 48 dbp->close = __db_close; 49 dbp->cursor = __db_cursor; 50 dbp->del = NULL; /* !!! Must be set by access method. */ 51 dbp->fd = __db_fd; 52 dbp->get = __db_get; 53 dbp->join = __db_join; 54 dbp->put = __db_put; 55 dbp->stat = NULL; /* !!! Must be set by access method. */ 56 dbp->sync = __db_sync; 57 58 return (0); 59 } 60 61 /* 62 * __db_cursor -- 63 * Allocate and return a cursor. 64 */ 65 static int 66 __db_cursor(dbp, txn, dbcp, flags) 67 DB *dbp; 68 DB_TXN *txn; 69 DBC **dbcp; 70 u_int32_t flags; 71 { 72 DBC *dbc, *adbc; 73 int ret; 74 db_lockmode_t mode; 75 u_int32_t op; 76 77 DB_PANIC_CHECK(dbp); 78 79 /* Take one from the free list if it's available. */ 80 DB_THREAD_LOCK(dbp); 81 if ((dbc = TAILQ_FIRST(&dbp->free_queue)) != NULL) 82 TAILQ_REMOVE(&dbp->free_queue, dbc, links); 83 else { 84 DB_THREAD_UNLOCK(dbp); 85 86 if ((ret = __os_calloc(1, sizeof(DBC), &dbc)) != 0) 87 return (ret); 88 89 dbc->dbp = dbp; 90 dbc->c_close = __db_c_close; 91 92 /* Set up locking information. */ 93 if (F_ISSET(dbp, DB_AM_LOCKING | DB_AM_CDB)) { 94 /* 95 * If we are not threaded, then there is no need to 96 * create new locker ids. We know that no one else 97 * is running concurrently using this DB, so we can 98 * take a peek at any cursors on the active queue. 99 */ 100 if (!F_ISSET(dbp, DB_AM_THREAD) && 101 (adbc = TAILQ_FIRST(&dbp->active_queue)) != NULL) 102 dbc->lid = adbc->lid; 103 else 104 if ((ret = lock_id(dbp->dbenv->lk_info, 105 &dbc->lid)) != 0) 106 goto err; 107 108 memcpy(dbc->lock.fileid, dbp->fileid, DB_FILE_ID_LEN); 109 if (F_ISSET(dbp, DB_AM_CDB)) { 110 dbc->lock_dbt.size = DB_FILE_ID_LEN; 111 dbc->lock_dbt.data = dbc->lock.fileid; 112 } else { 113 dbc->lock_dbt.size = sizeof(dbc->lock); 114 dbc->lock_dbt.data = &dbc->lock; 115 } 116 } 117 118 switch (dbp->type) { 119 case DB_BTREE: 120 case DB_RECNO: 121 if ((ret = __bam_c_init(dbc)) != 0) 122 goto err; 123 break; 124 case DB_HASH: 125 if ((ret = __ham_c_init(dbc)) != 0) 126 goto err; 127 break; 128 default: 129 ret = EINVAL; 130 goto err; 131 } 132 133 DB_THREAD_LOCK(dbp); 134 } 135 136 if ((dbc->txn = txn) == NULL) 137 dbc->locker = dbc->lid; 138 else 139 dbc->locker = txn->txnid; 140 141 TAILQ_INSERT_TAIL(&dbp->active_queue, dbc, links); 142 DB_THREAD_UNLOCK(dbp); 143 144 /* 145 * If this is the concurrent DB product, then we do all locking 146 * in the interface, which is right here. 147 */ 148 if (F_ISSET(dbp, DB_AM_CDB)) { 149 op = LF_ISSET(DB_OPFLAGS_MASK); 150 mode = (op == DB_WRITELOCK) ? DB_LOCK_WRITE : 151 (LF_ISSET(DB_RMW) ? DB_LOCK_IWRITE : DB_LOCK_READ); 152 if ((ret = lock_get(dbp->dbenv->lk_info, dbc->locker, 0, 153 &dbc->lock_dbt, mode, &dbc->mylock)) != 0) { 154 (void)__db_c_close(dbc); 155 return (EAGAIN); 156 } 157 if (LF_ISSET(DB_RMW)) 158 F_SET(dbc, DBC_RMW); 159 if (op == DB_WRITELOCK) 160 F_SET(dbc, DBC_WRITER); 161 } 162 163 *dbcp = dbc; 164 return (0); 165 166 err: __os_free(dbc, sizeof(*dbc)); 167 return (ret); 168 } 169 170 /* 171 * __db_c_close -- 172 * Close the cursor (recycle for later use). 173 */ 174 static int 175 __db_c_close(dbc) 176 DBC *dbc; 177 { 178 DB *dbp; 179 int ret, t_ret; 180 181 dbp = dbc->dbp; 182 183 DB_PANIC_CHECK(dbp); 184 185 ret = 0; 186 187 /* 188 * We cannot release the lock until after we've called the 189 * access method specific routine, since btrees may have pending 190 * deletes. 191 */ 192 193 /* Remove the cursor from the active queue. */ 194 DB_THREAD_LOCK(dbp); 195 TAILQ_REMOVE(&dbp->active_queue, dbc, links); 196 DB_THREAD_UNLOCK(dbp); 197 198 /* Call the access specific cursor close routine. */ 199 if ((t_ret = dbc->c_am_close(dbc)) != 0 && ret == 0) 200 t_ret = ret; 201 202 /* Release the lock. */ 203 if (F_ISSET(dbc->dbp, DB_AM_CDB) && dbc->mylock != LOCK_INVALID) { 204 ret = lock_put(dbc->dbp->dbenv->lk_info, dbc->mylock); 205 dbc->mylock = LOCK_INVALID; 206 } 207 208 /* Clean up the cursor. */ 209 dbc->flags = 0; 210 211 #ifdef DEBUG 212 /* 213 * Check for leftover locks, unless we're running with transactions. 214 * 215 * If we're running tests, display any locks currently held. It's 216 * possible that some applications may hold locks for long periods, 217 * e.g., conference room locks, but the DB tests should never close 218 * holding locks. 219 */ 220 if (F_ISSET(dbp, DB_AM_LOCKING) && dbc->lid == dbc->locker) { 221 DB_LOCKREQ request; 222 223 request.op = DB_LOCK_DUMP; 224 if ((t_ret = lock_vec(dbp->dbenv->lk_info, 225 dbc->locker, 0, &request, 1, NULL)) != 0 && ret == 0) 226 ret = EAGAIN; 227 } 228 #endif 229 /* Move the cursor to the free queue. */ 230 DB_THREAD_LOCK(dbp); 231 TAILQ_INSERT_TAIL(&dbp->free_queue, dbc, links); 232 DB_THREAD_UNLOCK(dbp); 233 234 return (ret); 235 } 236 237 #ifdef DEBUG 238 /* 239 * __db_cprint -- 240 * Display the current cursor list. 241 * 242 * PUBLIC: int __db_cprint __P((DB *)); 243 */ 244 int 245 __db_cprint(dbp) 246 DB *dbp; 247 { 248 static const FN fn[] = { 249 { DBC_RECOVER, "recover" }, 250 { DBC_RMW, "read-modify-write" }, 251 { 0 }, 252 }; 253 DBC *dbc; 254 255 DB_THREAD_LOCK(dbp); 256 for (dbc = TAILQ_FIRST(&dbp->active_queue); 257 dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) { 258 fprintf(stderr, 259 "%#0x: dbp: %#0x txn: %#0x lid: %lu locker: %lu", 260 (u_int)dbc, (u_int)dbc->dbp, (u_int)dbc->txn, 261 (u_long)dbc->lid, (u_long)dbc->locker); 262 __db_prflags(dbc->flags, fn, stderr); 263 fprintf(stderr, "\n"); 264 } 265 DB_THREAD_UNLOCK(dbp); 266 267 return (0); 268 } 269 #endif /* DEBUG */ 270 271 /* 272 * __db_c_destroy -- 273 * Destroy the cursor. 274 * 275 * PUBLIC: int __db_c_destroy __P((DBC *)); 276 */ 277 int 278 __db_c_destroy(dbc) 279 DBC *dbc; 280 { 281 DB *dbp; 282 int ret; 283 284 dbp = dbc->dbp; 285 286 /* Remove the cursor from the free queue. */ 287 DB_THREAD_LOCK(dbp); 288 TAILQ_REMOVE(&dbp->free_queue, dbc, links); 289 DB_THREAD_UNLOCK(dbp); 290 291 /* Call the access specific cursor destroy routine. */ 292 ret = dbc->c_am_destroy == NULL ? 0 : dbc->c_am_destroy(dbc); 293 294 /* Free up allocated memory. */ 295 if (dbc->rkey.data != NULL) 296 __os_free(dbc->rkey.data, dbc->rkey.ulen); 297 if (dbc->rdata.data != NULL) 298 __os_free(dbc->rdata.data, dbc->rdata.ulen); 299 __os_free(dbc, sizeof(*dbc)); 300 301 return (0); 302 } 303 304 /* 305 * db_fd -- 306 * Return a file descriptor for flock'ing. 307 */ 308 static int 309 __db_fd(dbp, fdp) 310 DB *dbp; 311 int *fdp; 312 { 313 DB_PANIC_CHECK(dbp); 314 315 /* 316 * XXX 317 * Truly spectacular layering violation. 318 */ 319 return (__mp_xxx_fd(dbp->mpf, fdp)); 320 } 321 322 /* 323 * __db_get -- 324 * Return a key/data pair. 325 */ 326 static int 327 __db_get(dbp, txn, key, data, flags) 328 DB *dbp; 329 DB_TXN *txn; 330 DBT *key, *data; 331 u_int32_t flags; 332 { 333 DBC *dbc; 334 int ret, t_ret; 335 336 DB_PANIC_CHECK(dbp); 337 338 if ((ret = __db_getchk(dbp, key, data, flags)) != 0) 339 return (ret); 340 341 if ((ret = dbp->cursor(dbp, txn, &dbc, 0)) != 0) 342 return (ret); 343 344 DEBUG_LREAD(dbc, txn, "__db_get", key, NULL, flags); 345 346 ret = dbc->c_get(dbc, key, data, 347 flags == 0 || flags == DB_RMW ? flags | DB_SET : flags); 348 349 if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0) 350 ret = t_ret; 351 352 return (ret); 353 } 354 355 /* 356 * __db_put -- 357 * Store a key/data pair. 358 */ 359 static int 360 __db_put(dbp, txn, key, data, flags) 361 DB *dbp; 362 DB_TXN *txn; 363 DBT *key, *data; 364 u_int32_t flags; 365 { 366 DBC *dbc; 367 DBT tdata; 368 int ret, t_ret; 369 370 DB_PANIC_CHECK(dbp); 371 372 if ((ret = __db_putchk(dbp, key, data, 373 flags, F_ISSET(dbp, DB_AM_RDONLY), F_ISSET(dbp, DB_AM_DUP))) != 0) 374 return (ret); 375 376 if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0) 377 return (ret); 378 379 DEBUG_LWRITE(dbc, txn, "__db_put", key, data, flags); 380 381 if (flags == DB_NOOVERWRITE) { 382 /* 383 * Set DB_DBT_USERMEM, this might be a threaded application and 384 * the flags checking will catch us. We don't want the actual 385 * data, so request a partial of length 0. 386 */ 387 memset(&tdata, 0, sizeof(tdata)); 388 F_SET(&tdata, DB_DBT_USERMEM | DB_DBT_PARTIAL); 389 if ((ret = dbc->c_get(dbc, key, &tdata, DB_SET | DB_RMW)) == 0) 390 ret = DB_KEYEXIST; 391 else if (ret == DB_NOTFOUND) 392 ret = 0; 393 } 394 if (ret == 0) 395 ret = dbc->c_put(dbc, key, data, DB_KEYLAST); 396 397 if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0) 398 ret = t_ret; 399 400 return (ret); 401 } 402 403 /* 404 * __db_sync -- 405 * Flush the database cache. 406 * 407 * PUBLIC: int __db_sync __P((DB *, u_int32_t)); 408 */ 409 int 410 __db_sync(dbp, flags) 411 DB *dbp; 412 u_int32_t flags; 413 { 414 int ret; 415 416 DB_PANIC_CHECK(dbp); 417 418 if ((ret = __db_syncchk(dbp, flags)) != 0) 419 return (ret); 420 421 /* If it wasn't possible to modify the file, we're done. */ 422 if (F_ISSET(dbp, DB_AM_INMEM | DB_AM_RDONLY)) 423 return (0); 424 425 /* Flush any dirty pages from the cache to the backing file. */ 426 if ((ret = memp_fsync(dbp->mpf)) == DB_INCOMPLETE) 427 ret = 0; 428 429 return (ret); 430 } 431