db

qam.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1999, 2000
00005  * Sleepycat Software. All rights reserved.
00006  */
00007 
00008 #include "config.h"
00009 
00010 #ifndef lint
00011 static const char revid[] = "$Id: qam_8c-source.html,v 1.1 2008年06月08日 10:21:34 sebdiaz Exp $";
00012 #endif /* not lint */
00013 
00014 #ifndef NO_SYSTEM_INCLUDES
00015 #include <sys/types.h>
00016 
00017 #include <errno.h>
00018 #include <string.h>
00019 #endif
00020 
00021 #include "db_int.h"
00022 #include "db_page.h"
00023 #include "db_shash.h"
00024 #include "db_am.h"
00025 #include "lock.h"
00026 #include "btree.h"
00027 #include "qam.h"
00028 
00029 static int __qam_c_close __P((DBC *, db_pgno_t, int *));
00030 static int __qam_c_del __P((DBC *));
00031 static int __qam_c_destroy __P((DBC *));
00032 static int __qam_c_get __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *));
00033 static int __qam_c_put __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *));
00034 static int __qam_getno __P((DB *, const DBT *, db_recno_t *));
00035 static int __qam_nrecs __P((DBC *, db_recno_t *, db_recno_t *));
00036 static int __qam_position
00037 __P((DBC *, db_recno_t *, db_lockmode_t, db_recno_t, int *));
00038 
00039 /*
00040  * __qam_nrecs --
00041  * Return the record number for the head and tail of the queue.
00042  */
00043 static int
00044 __qam_nrecs(dbc, rep, start)
00045 DBC *dbc;
00046 db_recno_t *rep, *start;
00047 {
00048 DB *dbp;
00049 DB_LOCK lock;
00050 QMETA *meta;
00051 db_pgno_t pg;
00052 int ret, t_ret;
00053 
00054 dbp = dbc->dbp;
00055 
00056 pg = ((QUEUE *)dbp->q_internal)->q_meta;
00057 if ((ret = CDB___db_lget(dbc, 0, pg, DB_LOCK_READ, 0, &lock)) != 0)
00058 return (ret);
00059 if ((ret = CDB_memp_fget(dbp->mpf, &pg, 0, &meta)) != 0) {
00060 /* We did not fetch it, we can release the lock. */
00061 (void)__LPUT(dbc, lock);
00062 return (ret);
00063 }
00064 
00065 *rep = meta->cur_recno;
00066 *start = meta->start;
00067 
00068 ret = CDB_memp_fput(dbp->mpf, meta, 0);
00069 
00070 /* Don't hold the meta page long term. */
00071 if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0)
00072 ret = t_ret;
00073 
00074 return (ret);
00075 }
00076 
00077 /*
00078  * __qam_position --
00079  * Position a queued access method cursor at a record. This returns
00080  * the page locked. *exactp will be set if the record is valid.
00081  */
00082 static int
00083 __qam_position(dbc, recnop, lock_mode, start, exactp)
00084 DBC *dbc; /* open cursor */
00085 db_recno_t *recnop; /* pointer to recno to find */
00086 db_lockmode_t lock_mode;/* locking: read or write */
00087 db_recno_t start; /* meta.start */
00088 int *exactp; /* indicate if it was found */
00089 {
00090 QUEUE_CURSOR *cp;
00091 DB *dbp;
00092 QAMDATA *qp;
00093 db_pgno_t pg;
00094 int ret;
00095 
00096 dbp = dbc->dbp;
00097 cp = (QUEUE_CURSOR *)dbc->internal;
00098 
00099 /* Fetch the page for this recno. */
00100 pg = QAM_RECNO_PAGE(dbp, start, *recnop);
00101 
00102 if ((ret = CDB___db_lget(dbc, 0, pg, lock_mode, 0, &cp->lock)) != 0)
00103 return (ret);
00104 if ((ret = CDB_memp_fget(dbp->mpf, &pg,
00105 lock_mode == DB_LOCK_WRITE ? DB_MPOOL_CREATE : 0,
00106 &cp->page)) != 0) {
00107 /* We did not fetch it, we can release the lock. */
00108 (void)__LPUT(dbc, cp->lock);
00109 cp->lock.off = LOCK_INVALID;
00110 return (ret);
00111 }
00112 cp->pgno = pg;
00113 cp->indx = QAM_RECNO_INDEX(dbp, pg, start, *recnop);
00114 
00115 if (PGNO(cp->page) == 0) {
00116 if (F_ISSET(dbp, DB_AM_RDONLY)) {
00117 *exactp = 0;
00118 return (0);
00119 }
00120 PGNO(cp->page) = pg;
00121 TYPE_SET(cp->page, P_QAMDATA);
00122 }
00123 
00124 qp = QAM_GET_RECORD(dbp, cp->page, cp->indx);
00125 *exactp = F_ISSET(qp, QAM_VALID);
00126 
00127 return (ret);
00128 }
00129 
00130 /*
00131  * CDB___qam_pitem --
00132  * Put an item on a queue page. Copy the data to the page and set the
00133  * VALID and SET bits. If logging and the record was previously set,
00134  * log that data, otherwise just log the new data.
00135  *
00136  * pagep must be write locked
00137  *
00138  * PUBLIC: int CDB___qam_pitem
00139  * PUBLIC: __P((DBC *, QPAGE *, u_int32_t, db_recno_t, DBT *));
00140  */
00141 int
00142 CDB___qam_pitem(dbc, pagep, indx, recno, data)
00143 DBC *dbc;
00144 QPAGE *pagep;
00145 u_int32_t indx;
00146 db_recno_t recno;
00147 DBT *data;
00148 {
00149 DB *dbp;
00150 DBT olddata, pdata, *datap;
00151 QAMDATA *qp;
00152 QUEUE *t;
00153 u_int32_t size;
00154 u_int8_t *dest, *p;
00155 int alloced, ret;
00156 
00157 alloced = ret = 0;
00158 
00159 dbp = dbc->dbp;
00160 t = (QUEUE *)dbp->q_internal;
00161 
00162 if (data->size > t->re_len)
00163 goto len_err;
00164 
00165 qp = QAM_GET_RECORD(dbp, pagep, indx);
00166 
00167 p = qp->data;
00168 size = data->size;
00169 datap = data;
00170 if (F_ISSET(data, DB_DBT_PARTIAL)) {
00171 if (data->doff + data->dlen > t->re_len) {
00172 alloced = data->dlen;
00173 goto len_err;
00174 }
00175 if (data->size != data->dlen) {
00176 len_err: CDB___db_err(dbp->dbenv,
00177 "Length improper for fixed length record %lu",
00178 (u_long)(alloced ? alloced : data->size));
00179 return (EINVAL);
00180 }
00181 if (data->size == t->re_len)
00182 goto no_partial;
00183 
00184 /*
00185  * If we are logging, then we have to build the record
00186  * first, otherwise, we can simply drop the change
00187  * directly on the page. After this clause, make
00188  * sure that datap and p are set up correctly so that
00189  * copying datap into p does the right thing.
00190  *
00191  * Note, I am changing this so that if the existing
00192  * record is not valid, we create a complete record
00193  * to log so that both this and the recovery code is simpler.
00194  */
00195 
00196 if (DB_LOGGING(dbc) || !F_ISSET(qp, QAM_VALID)) {
00197 datap = &pdata;
00198 memset(datap, 0, sizeof(*datap));
00199 
00200 if ((ret = CDB___os_malloc(dbp->dbenv,
00201 t->re_len, NULL, &datap->data)) != 0)
00202 return (ret);
00203 alloced = 1;
00204 datap->size = t->re_len;
00205 
00206 /*
00207  * Construct the record if it's valid, otherwise set it
00208  * all to the pad character.
00209  */
00210 dest = datap->data;
00211 if (F_ISSET(qp, QAM_VALID))
00212 memcpy(dest, p, t->re_len);
00213 else
00214 memset(dest, t->re_pad, t->re_len);
00215 
00216 dest += data->doff;
00217 memcpy(dest, data->data, data->size);
00218 } else {
00219 datap = data;
00220 p += data->doff;
00221 }
00222 }
00223 
00224 no_partial:
00225 if (DB_LOGGING(dbc)) {
00226 olddata.size = 0;
00227 if (F_ISSET(qp, QAM_SET)) {
00228 olddata.data = qp->data;
00229 olddata.size = t->re_len;
00230 }
00231 if ((ret = CDB___qam_add_log(dbp->dbenv, dbc->txn, &LSN(pagep),
00232 0, dbp->log_fileid, &LSN(pagep), pagep->pgno,
00233 indx, recno, datap, qp->flags,
00234 olddata.size == 0 ? NULL : &olddata)) != 0)
00235 goto err;
00236 }
00237 
00238 F_SET(qp, QAM_VALID | QAM_SET);
00239 memcpy(p, datap->data, datap->size);
00240 if (!F_ISSET(data, DB_DBT_PARTIAL))
00241 memset(p + datap->size, t->re_pad, t->re_len - datap->size);
00242 
00243 err: if (alloced)
00244 CDB___os_free(datap->data, t->re_len);
00245 
00246 return (ret);
00247 }
00248 /*
00249  * __qam_c_put
00250  * Cursor put for queued access method.
00251  * BEFORE and AFTER cannot be specified.
00252  */
00253 static int
00254 __qam_c_put(dbc, key, data, flags, pgnop)
00255 DBC *dbc;
00256 DBT *key, *data;
00257 u_int32_t flags;
00258 db_pgno_t *pgnop;
00259 {
00260 QUEUE_CURSOR *cp;
00261 DB *dbp;
00262 DB_LOCK lock;
00263 QMETA *meta;
00264 db_pgno_t pg;
00265 db_recno_t new_cur, new_first;
00266 u_int32_t opcode;
00267 int exact, ret, t_ret;
00268 
00269 COMPQUIET(key, NULL);
00270 
00271 dbp = dbc->dbp;
00272 if (pgnop != NULL)
00273 *pgnop = PGNO_INVALID;
00274 
00275 cp = (QUEUE_CURSOR *)dbc->internal;
00276 
00277 /* Write lock the record. */
00278 if ((ret = CDB___db_lget(dbc,
00279 0, cp->recno, DB_LOCK_WRITE, DB_LOCK_RECORD, &lock)) != 0)
00280 return (ret);
00281 
00282 if ((ret = __qam_position(dbc,
00283 &cp->recno, DB_LOCK_WRITE, cp->start, &exact)) != 0) {
00284 /* We could not get the page, we can release the record lock. */
00285 __LPUT(dbc, lock);
00286 return (ret);
00287 }
00288 
00289 if (exact && flags == DB_NOOVERWRITE) {
00290 ret = __TLPUT(dbc, lock);
00291 /* Doing record locking, release the page lock */
00292 if ((t_ret = __LPUT(dbc, cp->lock)) == 0)
00293 cp->lock.off = LOCK_INVALID;
00294 else
00295 if (ret == 0)
00296 ret = t_ret;
00297 if ((t_ret = CDB_memp_fput(dbp->mpf, cp->page, 0)) != 0 && ret == 0)
00298 ret = t_ret;
00299 cp->page = NULL;
00300 return (ret == 0 ? DB_KEYEXIST : ret);
00301 }
00302 
00303 /* Put the item on the page. */
00304 ret = CDB___qam_pitem(dbc, (QPAGE *)cp->page, cp->indx, cp->recno, data);
00305 
00306 /* Doing record locking, release the page lock */
00307 if ((t_ret = __LPUT(dbc, cp->lock)) != 0 && ret == 0)
00308 ret = t_ret;
00309 if ((t_ret = CDB_memp_fput(dbp->mpf, cp->page, DB_MPOOL_DIRTY)) && ret == 0)
00310 ret = t_ret;
00311 cp->page = NULL;
00312 cp->lock = lock;
00313 cp->lock_mode = DB_LOCK_WRITE;
00314 if (ret != 0)
00315 return (ret);
00316 
00317 /* We may need to reset the head or tail of the queue. */
00318 pg = ((QUEUE *)dbp->q_internal)->q_meta;
00319 if ((ret = CDB___db_lget(dbc, 0, pg, DB_LOCK_WRITE, 0, &lock)) != 0)
00320 return (ret);
00321 if ((ret = CDB_memp_fget(dbp->mpf, &pg, 0, &meta)) != 0) {
00322 /* We did not fetch it, we can release the lock. */
00323 (void)__LPUT(dbc, lock);
00324 return (ret);
00325 }
00326 
00327 opcode = 0;
00328 new_cur = new_first = 0;
00329 
00330 if (cp->recno > meta->cur_recno) {
00331 new_cur = cp->recno;
00332 opcode |= QAM_SETCUR;
00333 }
00334 if (cp->recno < meta->first_recno || meta->first_recno < meta->start) {
00335 new_first = cp->recno;
00336 opcode |= QAM_SETFIRST;
00337 }
00338 
00339 if (opcode != 0 && DB_LOGGING(dbc)) {
00340 ret = CDB___qam_mvptr_log(dbp->dbenv, dbc->txn, &meta->dbmeta.lsn,
00341 0, opcode, dbp->log_fileid, meta->first_recno, new_first,
00342 meta->cur_recno, new_cur, &meta->dbmeta.lsn);
00343 }
00344 
00345 if (opcode & QAM_SETCUR)
00346 meta->cur_recno = cp->recno;
00347 if (opcode & QAM_SETFIRST)
00348 meta->first_recno = cp->recno;
00349 
00350 if ((t_ret =
00351 CDB_memp_fput(dbp->mpf, meta, opcode != 0 ? DB_MPOOL_DIRTY : 0)) != 0 &&
00352 ret == 0)
00353 ret = t_ret;
00354 
00355 /* Don't hold the meta page long term. */
00356 if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0)
00357 ret = t_ret;
00358 return (ret);
00359 }
00360 
00361 /*
00362  * CDB___qam_put --
00363  * Add a record to the queue.
00364  * If we are doing anything but appending, just call qam_c_put to do the
00365  * work. Otherwise we fast path things here.
00366  *
00367  * PUBLIC: int CDB___qam_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
00368  */
00369 int
00370 CDB___qam_put(dbp, txn, key, data, flags)
00371 DB *dbp;
00372 DB_TXN *txn;
00373 DBT *key, *data;
00374 u_int32_t flags;
00375 {
00376 QUEUE_CURSOR *cp;
00377 DBC *dbc;
00378 DB_LOCK lock;
00379 QMETA *meta;
00380 QPAGE *page;
00381 db_pgno_t pg;
00382 db_recno_t recno, start, total;
00383 int ret, t_ret;
00384 
00385 PANIC_CHECK(dbp->dbenv);
00386 
00387 /* Allocate a cursor. */
00388 if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0)
00389 return (ret);
00390 
00391 DEBUG_LWRITE(dbc, txn, "qam_put", key, data, flags);
00392 
00393 cp = (QUEUE_CURSOR *)dbc->internal;
00394 
00395 /* Check for invalid flags. */
00396 if ((ret = CDB___db_putchk(dbp,
00397 key, data, flags, F_ISSET(dbp, DB_AM_RDONLY), 0)) != 0)
00398 goto done;
00399 
00400 /* If not appending, then just call the cursor routine */
00401 if (flags != DB_APPEND) {
00402 if ((ret = __qam_getno(dbp, key, &cp->recno)) != 0)
00403 goto done;
00404 __qam_nrecs(dbc, &total, &cp->start);
00405 
00406 ret = __qam_c_put(dbc, NULL, data, flags, NULL);
00407 goto done;
00408 }
00409 
00410 /* Write lock the meta page. */
00411 pg = ((QUEUE *)dbp->q_internal)->q_meta;
00412 if ((ret = CDB___db_lget(dbc, 0, pg, DB_LOCK_WRITE, 0, &lock)) != 0)
00413 goto done;
00414 if ((ret = CDB_memp_fget(dbp->mpf, &pg, 0, &meta)) != 0) {
00415 /* We did not fetch it, we can release the lock. */
00416 (void)__LPUT(dbc, lock);
00417 goto done;
00418 }
00419 
00420 /* Record that we are going to allocate a record. */
00421 if (DB_LOGGING(dbc)) {
00422 CDB___qam_inc_log(dbp->dbenv,
00423 txn, &meta->dbmeta.lsn,
00424 0, dbp->log_fileid, &meta->dbmeta.lsn);
00425 }
00426 
00427 /* Get the next record number. */
00428 recno = ++meta->cur_recno;
00429 start = meta->start;
00430 
00431 if (meta->first_recno < meta->start || meta->first_recno > recno)
00432 meta->first_recno = recno;
00433 
00434 /* Release the meta page. */
00435 if ((ret = CDB_memp_fput(dbp->mpf, meta, DB_MPOOL_DIRTY)) != 0) {
00436 (void)__LPUT(dbc, lock);
00437 goto done;
00438 }
00439 
00440 /* Lock the record and release meta page lock. */
00441 if ((ret = CDB___db_lget(dbc,
00442 1, recno, DB_LOCK_WRITE, DB_LOCK_RECORD, &lock)) != 0)
00443 goto done;
00444 
00445 cp->lock = lock;
00446 cp->lock_mode = DB_LOCK_WRITE;
00447 
00448 pg = QAM_RECNO_PAGE(dbp, start, recno);
00449 
00450 /* Fetch and write lock the data page. */
00451 if ((ret = CDB___db_lget(dbc, 0, pg, DB_LOCK_WRITE, 0, &lock)) != 0)
00452 goto done;
00453 if ((ret = CDB_memp_fget(dbp->mpf, &pg, DB_MPOOL_CREATE, &page)) != 0) {
00454 /* We did not fetch it, we can release the lock. */
00455 (void)__LPUT(dbc, lock);
00456 goto done;
00457 }
00458 
00459 /* See if this is a new page. */
00460 if (page->pgno == 0) {
00461 page->pgno = pg;
00462 page->type = P_QAMDATA;
00463 }
00464 
00465 /* Put the item on the page and log it. */
00466 ret = CDB___qam_pitem(dbc, page,
00467 QAM_RECNO_INDEX(dbp, pg, start, recno), recno, data);
00468 
00469 /* Doing record locking, release the page lock */
00470 if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0)
00471 ret = t_ret;
00472 
00473 if ((t_ret
00474 = CDB_memp_fput(dbp->mpf, page, DB_MPOOL_DIRTY)) != 0 && ret == 0)
00475 ret = t_ret;
00476 
00477 
00478 /* Return the record number to the user. */
00479 ret = CDB___db_retcopy(dbp, key,
00480 &recno, sizeof(recno), &dbc->rkey.data, &dbc->rkey.ulen);
00481 
00482 done:
00483 /* Discard the cursor. */
00484 if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
00485 ret = t_ret;
00486 
00487 return (ret);
00488 }
00489 
00490 /*
00491  * __qam_c_del --
00492  * Qam cursor->am_del function
00493  */
00494 static int
00495 __qam_c_del(dbc)
00496 DBC *dbc;
00497 {
00498 QUEUE_CURSOR *cp;
00499 DB *dbp;
00500 DBT data;
00501 DB_LOCK lock;
00502 PAGE *pagep;
00503 QAMDATA *qp;
00504 db_recno_t start;
00505 db_recno_t total;
00506 int exact, ret, t_ret;
00507 
00508 dbp = dbc->dbp;
00509 cp = (QUEUE_CURSOR *)dbc->internal;
00510 ret = 0;
00511 
00512 __qam_nrecs(dbc, &total, &cp->start);
00513 start = cp->start;
00514 
00515 if (cp->recno > total) {
00516 ret = DB_NOTFOUND;
00517 return (ret);
00518 }
00519 
00520 if ((ret = CDB___db_lget(dbc,
00521 0, cp->recno, DB_LOCK_WRITE, DB_LOCK_RECORD, &lock)) != 0)
00522 return (ret);
00523 
00524 cp->lock_mode = DB_LOCK_WRITE;
00525 /* Find the record ; delete only deletes exact matches. */
00526 if ((ret = __qam_position(dbc,
00527 &cp->recno, DB_LOCK_WRITE, start, &exact)) != 0) {
00528 cp->lock = lock;
00529 return (ret);
00530 }
00531 if (!exact) {
00532 ret = DB_NOTFOUND;
00533 goto err1;
00534 }
00535 
00536 pagep = cp->page;
00537 qp = QAM_GET_RECORD(dbp, pagep, cp->indx);
00538 
00539 if (DB_LOGGING(dbc)) {
00540 data.size = ((QUEUE *)dbp->q_internal)->re_len;
00541 data.data = qp->data;
00542 if ((ret = CDB___qam_del_log(dbp->dbenv, dbc->txn,
00543 &LSN(pagep), 0, dbp->log_fileid, &LSN(pagep),
00544 pagep->pgno, cp->indx, cp->recno)) != 0)
00545 goto err1;
00546 }
00547 
00548 F_CLR(qp, QAM_VALID);
00549 
00550 err1:
00551 if ((t_ret = CDB_memp_fput(
00552 dbp->mpf, cp->page, ret == 0 ? DB_MPOOL_DIRTY : 0)) != 0)
00553 return (ret ? ret : t_ret);
00554 cp->page = NULL;
00555 /* Doing record locking, release the page lock */
00556 if ((t_ret = __LPUT(dbc, cp->lock)) != 0) {
00557 cp->lock = lock;
00558 return (ret ? ret : t_ret);
00559 }
00560 cp->lock = lock;
00561 return (ret);
00562 }
00563 
00564 /*
00565  * CDB___qam_delete --
00566  * Queue db->del function.
00567  *
00568  * PUBLIC: int CDB___qam_delete __P((DB *, DB_TXN *, DBT *, u_int32_t));
00569  */
00570 int
00571 CDB___qam_delete(dbp, txn, key, flags)
00572 DB *dbp;
00573 DB_TXN *txn;
00574 DBT *key;
00575 u_int32_t flags;
00576 {
00577 QUEUE_CURSOR *cp;
00578 DBC *dbc;
00579 int ret, t_ret;
00580 
00581 PANIC_CHECK(dbp->dbenv);
00582 
00583 /* Check for invalid flags. */
00584 if ((ret =
00585 CDB___db_delchk(dbp, key, flags, F_ISSET(dbp, DB_AM_RDONLY))) != 0)
00586 return (ret);
00587 
00588 /* Acquire a cursor. */
00589 if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0)
00590 return (ret);
00591 
00592 DEBUG_LWRITE(dbc, txn, "qam_delete", key, NULL, flags);
00593 
00594 cp = (QUEUE_CURSOR *)dbc->internal;
00595 if ((ret = __qam_getno(dbp, key, &cp->recno)) != 0)
00596 goto err;
00597 
00598 ret = __qam_c_del(dbc);
00599 
00600 /* Release the cursor. */
00601 err: if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
00602 ret = t_ret;
00603 
00604 return (ret);
00605 }
00606 
00607 /*
00608  * __qam_c_get --
00609  * Queue cursor->c_get function.
00610  */
00611 static int
00612 __qam_c_get(dbc, key, data, flags, pgnop)
00613 DBC *dbc;
00614 DBT *key, *data;
00615 u_int32_t flags;
00616 db_pgno_t *pgnop;
00617 {
00618 QUEUE_CURSOR *cp;
00619 DB *dbp;
00620 DB_LOCK lock, pglock, metalock, save_lock;
00621 DBT tmp;
00622 PAGE *pg;
00623 QAMDATA *qp;
00624 QMETA *meta;
00625 db_indx_t save_indx;
00626 db_lockmode_t lock_mode;
00627 db_pgno_t metapno, save_page;
00628 db_recno_t start, first, skipped, save_recno;
00629 int exact, is_first, locked, ret, t_ret, with_delete;
00630 int put_mode, meta_dirty;
00631 
00632 cp = (QUEUE_CURSOR *)dbc->internal;
00633 dbp = dbc->dbp;
00634 
00635 PANIC_CHECK(dbp->dbenv);
00636 
00637 with_delete = 0;
00638 lock_mode = DB_LOCK_READ;
00639 put_mode = 0;
00640 t_ret = 0;
00641 *pgnop = 0;
00642 
00643 if (F_ISSET(dbc, DBC_RMW))
00644 lock_mode = DB_LOCK_WRITE;
00645 
00646 if (flags == DB_CONSUME) {
00647 with_delete = 1;
00648 flags = DB_FIRST;
00649 lock_mode = DB_LOCK_WRITE;
00650 }
00651 
00652 DEBUG_LREAD(dbc, dbc->txn, "qam_c_get",
00653 flags == DB_SET || flags == DB_SET_RANGE ? key : NULL, NULL, flags);
00654 
00655 is_first = 0;
00656 
00657 /* get the meta page */
00658 metapno = ((QUEUE *)dbp->q_internal)->q_meta;
00659 if ((ret = CDB___db_lget(dbc, 0, metapno, lock_mode, 0, &metalock)) != 0)
00660 return (ret);
00661 locked = 1;
00662 if ((ret = CDB_memp_fget(dbp->mpf, &metapno, 0, &meta)) != 0) {
00663 /* We did not fetch it, we can release the lock. */
00664 (void)__LPUT(dbc, metalock);
00665 return (ret);
00666 }
00667 
00668 skipped = 0;
00669 
00670 /* Make lint and friends happy. */
00671 first = 0;
00672 meta_dirty = 0;
00673 
00674 /* Release any previous lock if not in a transaction. */
00675 if (cp->lock.off != LOCK_INVALID) {
00676 (void)__TLPUT(dbc, cp->lock);
00677 cp->lock.off = LOCK_INVALID;
00678 }
00679 
00680 retry: /* Update the record number. */
00681 cp->start = start = meta->start;
00682 switch (flags) {
00683 case DB_CURRENT:
00684 break;
00685 case DB_NEXT_DUP:
00686 ret = DB_NOTFOUND;
00687 goto err;
00688 /* NOTREACHED */
00689 case DB_NEXT:
00690 case DB_NEXT_NODUP:
00691 if (cp->recno != RECNO_OOB) {
00692 ++cp->recno;
00693 break;
00694 }
00695 /* FALLTHROUGH */
00696 case DB_FIRST:
00697 flags = DB_NEXT;
00698 is_first = 1;
00699 
00700 /* get the first record number */
00701 cp->recno = first = meta->first_recno;
00702 
00703 /* if we will delete it, then increment */
00704 if (with_delete && first < meta->cur_recno) {
00705 if (DB_LOGGING(dbc))
00706 CDB___qam_incfirst_log(dbp->dbenv, dbc->txn,
00707 &LSN(meta), 0,
00708 dbp->log_fileid, first);
00709 meta->first_recno++;
00710 meta_dirty = 1;
00711 }
00712 
00713 break;
00714 case DB_PREV:
00715 case DB_PREV_NODUP:
00716 if (cp->recno != RECNO_OOB) {
00717 if (cp->recno <= meta->first_recno) {
00718 ret = DB_NOTFOUND;
00719 goto err;
00720 }
00721 --cp->recno;
00722 break;
00723 }
00724 /* FALLTHROUGH */
00725 case DB_LAST:
00726 cp->recno = meta->cur_recno;
00727 if (cp->recno == 0) {
00728 ret = DB_NOTFOUND;
00729 goto err;
00730 }
00731 break;
00732 case DB_GET_BOTH:
00733 case DB_SET:
00734 case DB_SET_RANGE:
00735 if ((ret = __qam_getno(dbp, key, &cp->recno)) != 0)
00736 goto err;
00737 break;
00738 default:
00739 ret = CDB___db_unknown_flag(dbp->dbenv, "__qam_c_get", flags);
00740 goto err;
00741 }
00742 
00743 if (cp->recno > meta->cur_recno || cp->recno < start) {
00744 ret = DB_NOTFOUND;
00745 pg = NULL;
00746 if (skipped)
00747 goto undo_meta;
00748 goto err;
00749 }
00750 
00751 /* Don't hold the meta page long term. */
00752 if (locked) {
00753 if ((ret = __LPUT(dbc, metalock)) != 0)
00754 goto err;
00755 locked = 0;
00756 }
00757 
00758 /* Lock the record. */
00759 if ((ret = CDB___db_lget(dbc, 0, cp->recno, lock_mode,
00760 with_delete ? DB_LOCK_NOWAIT | DB_LOCK_RECORD : DB_LOCK_RECORD,
00761 &lock)) == DB_LOCK_NOTGRANTED && with_delete) {
00762 /*
00763  * In the DB_CONSUME case we skip the locked
00764  * record, someone else will pick it up.
00765  *
00766  */
00767 is_first = 0;
00768 if (skipped == 0)
00769 skipped = cp->recno;
00770 goto retry;
00771 }
00772 
00773 if (ret != 0)
00774 goto err;
00775 
00776 /*
00777  * In the DB_FIRST or DB_LAST cases we must wait and then start over
00778  * since the first/last may have moved while we slept.
00779  * We release our locks and try again.
00780  */
00781 if ((!with_delete && is_first) || flags == DB_LAST) {
00782 if ((ret =
00783 CDB___db_lget(dbc, 0, metapno, lock_mode, 0, &metalock)) != 0)
00784 goto err;
00785 if (cp->recno !=
00786 (is_first ? meta->first_recno : meta->cur_recno)) {
00787 __LPUT(dbc, lock);
00788 if (is_first)
00789 flags = DB_FIRST;
00790 locked = 1;
00791 goto retry;
00792 }
00793 /* Don't hold the meta page long term. */
00794 if ((ret = __LPUT(dbc, metalock)) != 0)
00795 goto err;
00796 }
00797 
00798 /* Position the cursor on the record. */
00799 if ((ret =
00800 __qam_position(dbc, &cp->recno, lock_mode, start, &exact)) != 0) {
00801 /* We cannot get the page, release the record lock. */
00802 (void)__LPUT(dbc, lock);
00803 goto err;
00804 }
00805 
00806 pg = cp->page;
00807 pglock = cp->lock;
00808 cp->lock = lock;
00809 cp->lock_mode = lock_mode;
00810 
00811 if (!exact) {
00812 if (flags == DB_NEXT || flags == DB_NEXT_NODUP
00813 || flags == DB_PREV || flags == DB_PREV_NODUP
00814 || flags == DB_LAST) {
00815 /* Release locks and try again. */
00816 (void)CDB_memp_fput(dbp->mpf, cp->page, 0);
00817 cp->page = NULL;
00818 (void)__LPUT(dbc, pglock);
00819 (void)__LPUT(dbc, cp->lock);
00820 cp->lock.off = LOCK_INVALID;
00821 if (flags == DB_LAST)
00822 flags = DB_PREV;
00823 if (!with_delete)
00824 is_first = 0;
00825 goto retry;
00826 }
00827 /* this is for the SET and SET_RANGE cases */
00828 ret = DB_KEYEMPTY;
00829 goto err1;
00830 }
00831 
00832 /* Return the key if the user didn't give us one. */
00833 if (flags != DB_SET && flags != DB_GET_BOTH &&
00834 (ret = CDB___db_retcopy(dbp, key, &cp->recno, sizeof(cp->recno),
00835 &dbc->rkey.data, &dbc->rkey.ulen)) != 0) {
00836 if (with_delete)
00837 goto undo_meta;
00838 else
00839 goto err1;
00840 }
00841 F_SET(key, DB_DBT_ISSET);
00842 
00843 qp = QAM_GET_RECORD(dbp, pg, cp->indx);
00844 
00845 /* Return the data item. */
00846 if (flags == DB_GET_BOTH) {
00847 /*
00848  * Need to compare
00849  */
00850 tmp.data = qp->data;
00851 tmp.size = ((QUEUE *)dbp->q_internal)->re_len;
00852 if ((ret = CDB___bam_defcmp(data, &tmp)) != 0) {
00853 ret = DB_NOTFOUND;
00854 goto err1;
00855 }
00856 }
00857 if ((ret = CDB___db_retcopy(dbp, data, qp->data,
00858 ((QUEUE *)dbp->q_internal)->re_len,
00859 &dbc->rdata.data, &dbc->rdata.ulen)) != 0) {
00860 if (with_delete)
00861 goto undo_meta;
00862 else
00863 goto err1;
00864 }
00865 F_SET(data, DB_DBT_ISSET);
00866 
00867 /* Finally, if we are doing DB_CONSUME mark the record. */
00868 if (with_delete) {
00869 if (DB_LOGGING(dbc))
00870 if ((ret = CDB___qam_del_log(dbp->dbenv, dbc->txn,
00871 &LSN(pg), 0, dbp->log_fileid, &LSN(pg),
00872 pg->pgno, cp->indx, cp->recno)) != 0)
00873 goto undo_meta;
00874 
00875 F_CLR(qp, QAM_VALID);
00876 put_mode = DB_MPOOL_DIRTY;
00877 
00878 /*
00879  * This code is responsible for correcting metadata.
00880  * There are 3 cases.
00881  * 1) We moved ahead more than one record.
00882  * 2) We did not actually delete cp->recno.
00883  * 3) We encountered at least one locked.
00884  * record and skipped them.
00885  */
00886 if (cp->recno != first) {
00887 if (0) {
00888 undo_meta: is_first = 0;
00889 }
00890 if (locked == 0 && (t_ret = CDB___db_lget(
00891 dbc, 0, metapno, lock_mode, 0, &metalock)) != 0)
00892 goto err1;
00893 
00894 if (is_first) {
00895 /*
00896  * Check to see if we moved past the first record,
00897  * if so update meta so others can start past the
00898  * deleted records.
00899  */
00900 if (meta->first_recno > first) {
00901 meta->first_recno = cp->recno;
00902 meta_dirty = 1;
00903 }
00904 }
00905 else if (skipped == 0) {
00906 /*
00907  * Error case: we did not actually delete the
00908  * record, restore meta_first so that it is at
00909  * least at or before cp->recno
00910  */
00911 if (meta->first_recno > cp->recno) {
00912 meta->first_recno = cp->recno;
00913 meta_dirty = 1;
00914 }
00915 }
00916 else if (meta->first_recno > skipped) {
00917 /*
00918  * We skipped some records because they were
00919  * locked. If the meta-data page reflects a
00920  * starting pointer after the skipped records
00921  * we need to move it back to the first record
00922  * that is not deleted or is sill locked.
00923  * Release locks as we go, we are only
00924  * reading to optimize future fetches.
00925  */
00926 first = meta->first_recno;
00927 /* Don't hold the meta page long term. */
00928 __LPUT(dbc, metalock);
00929 locked = 0;
00930 
00931 /* reverify the skipped record */
00932 save_page = cp->pgno;
00933 save_indx = cp->indx;
00934 save_recno = cp->recno;
00935 save_lock = cp->lock;
00936 do {
00937 t_ret = CDB___db_lget(dbc, 0, skipped,
00938 DB_LOCK_READ,
00939 DB_LOCK_NOWAIT | DB_LOCK_RECORD,
00940 &lock);
00941 if (t_ret == DB_LOCK_NOTGRANTED)
00942 break;
00943 if (t_ret != 0)
00944 goto err1;
00945 if ((t_ret =
00946 __qam_position(dbc, &skipped,
00947 DB_LOCK_READ,
00948 start, &exact)) != 0) {
00949 (void)__LPUT(dbc, lock);
00950 goto err1;
00951 }
00952 if ((t_ret = CDB_memp_fput(dbp->mpf,
00953 cp->page, put_mode)) != 0)
00954 goto err1;
00955 if ((t_ret =__LPUT(dbc, lock)) != 0)
00956 goto err1;
00957 if ((t_ret =
00958 __LPUT(dbc, cp->lock)) != 0)
00959 goto err1;
00960 if (exact)
00961 break;
00962 } while (++skipped <= first);
00963 
00964 t_ret = 0;
00965 if ((t_ret = CDB___db_lget(
00966 dbc, 0, metapno,
00967 lock_mode, 0, &metalock)) != 0)
00968 goto err1;
00969 
00970 if (meta->first_recno > skipped) {
00971 meta->first_recno = skipped;
00972 meta_dirty = 1;
00973 }
00974 cp->pgno = save_page;
00975 cp->indx = save_indx;
00976 cp->recno = save_recno;
00977 cp->lock = save_lock;
00978 }
00979 locked = 1;
00980 }
00981 }
00982 
00983 err1:
00984 cp->page = NULL;
00985 if (pg != NULL) {
00986 if (!ret)
00987 ret = t_ret;
00988 t_ret = CDB_memp_fput(dbp->mpf, pg, put_mode);
00989 
00990 if (!ret)
00991 ret = t_ret;
00992 /* Doing record locking, release the page lock */
00993 t_ret = __LPUT(dbc, pglock);
00994 }
00995 
00996 err:
00997 if (meta) {
00998 if (!ret)
00999 ret = t_ret;
01000 
01001 /* release the meta page */
01002 t_ret = CDB_memp_fput(
01003 dbp->mpf, meta, meta_dirty ? DB_MPOOL_DIRTY : 0);
01004 
01005 if (!ret)
01006 ret = t_ret;
01007 
01008 /* Don't hold the meta page long term. */
01009 if (locked)
01010 t_ret = __LPUT(dbc, metalock);
01011 }
01012 
01013 return (ret ? ret : t_ret);
01014 }
01015 
01016 /*
01017  * __qam_c_close --
01018  * Close down the cursor from a single use.
01019  */
01020 static int
01021 __qam_c_close(dbc, root_pgno, rmroot)
01022 DBC *dbc;
01023 db_pgno_t root_pgno;
01024 int *rmroot;
01025 {
01026 QUEUE_CURSOR *cp;
01027 
01028 COMPQUIET(root_pgno, 0);
01029 COMPQUIET(rmroot, NULL);
01030 
01031 cp = (QUEUE_CURSOR *)dbc->internal;
01032 
01033 /* Discard any locks not acquired inside of a transaction. */
01034 if (cp->lock.off != LOCK_INVALID) {
01035 (void)__TLPUT(dbc, cp->lock);
01036 cp->lock.off = LOCK_INVALID;
01037 }
01038 
01039 cp->page = NULL;
01040 cp->pgno = PGNO_INVALID;
01041 cp->indx = 0;
01042 cp->lock.off = LOCK_INVALID;
01043 cp->lock_mode = DB_LOCK_NG;
01044 cp->recno = RECNO_OOB;
01045 cp->flags = 0;
01046 
01047 return (0);
01048 }
01049 
01050 /*
01051  * CDB___qam_c_dup --
01052  * Duplicate a queue cursor, such that the new one holds appropriate
01053  * locks for the position of the original.
01054  *
01055  * PUBLIC: int CDB___qam_c_dup __P((DBC *, DBC *));
01056  */
01057 int
01058 CDB___qam_c_dup(orig_dbc, new_dbc)
01059 DBC *orig_dbc, *new_dbc;
01060 {
01061 QUEUE_CURSOR *orig, *new;
01062 
01063 orig = (QUEUE_CURSOR *)orig_dbc->internal;
01064 new = (QUEUE_CURSOR *)new_dbc->internal;
01065 
01066 new->recno = orig->recno;
01067 new->start = orig->start;
01068 
01069 /* reget the long term lock if we are not in a xact */
01070 if (orig_dbc->txn != NULL ||
01071 !STD_LOCKING(orig_dbc) || orig->lock.off == LOCK_INVALID)
01072 return (0);
01073 
01074 return (CDB___db_lget(new_dbc,
01075 0, new->recno, new->lock_mode, DB_LOCK_RECORD, &new->lock));
01076 }
01077 
01078 /*
01079  * CDB___qam_c_init
01080  *
01081  * PUBLIC: int CDB___qam_c_init __P((DBC *));
01082  */
01083 int
01084 CDB___qam_c_init(dbc)
01085 DBC *dbc;
01086 {
01087 QUEUE_CURSOR *cp;
01088 DB *dbp;
01089 int ret;
01090 
01091 dbp = dbc->dbp;
01092 
01093 /* Allocate the internal structure. */
01094 cp = (QUEUE_CURSOR *)dbc->internal;
01095 if (cp == NULL) {
01096 if ((ret =
01097 CDB___os_calloc(dbp->dbenv, 1, sizeof(QUEUE_CURSOR), &cp)) != 0)
01098 return (ret);
01099 dbc->internal = (DBC_INTERNAL *)cp;
01100 }
01101 
01102 /* Initialize methods. */
01103 dbc->c_close = CDB___db_c_close;
01104 dbc->c_count = CDB___db_c_count;
01105 dbc->c_del = CDB___db_c_del;
01106 dbc->c_dup = CDB___db_c_dup;
01107 dbc->c_get = CDB___db_c_get;
01108 dbc->c_put = CDB___db_c_put;
01109 dbc->c_am_close = __qam_c_close;
01110 dbc->c_am_del = __qam_c_del;
01111 dbc->c_am_destroy = __qam_c_destroy;
01112 dbc->c_am_get = __qam_c_get;
01113 dbc->c_am_put = __qam_c_put;
01114 dbc->c_am_writelock = NULL;
01115 
01116 return (0);
01117 }
01118 
01119 /*
01120  * __qam_c_destroy --
01121  * Close a single cursor -- internal version.
01122  */
01123 static int
01124 __qam_c_destroy(dbc)
01125 DBC *dbc;
01126 {
01127 /* Discard the structures. */
01128 CDB___os_free(dbc->internal, sizeof(QUEUE_CURSOR));
01129 
01130 return (0);
01131 }
01132 
01133 /*
01134  * __qam_getno --
01135  * Check the user's record number.
01136  */
01137 static int
01138 __qam_getno(dbp, key, rep)
01139 DB *dbp;
01140 const DBT *key;
01141 db_recno_t *rep;
01142 {
01143 if ((*rep = *(db_recno_t *)key->data) == 0) {
01144 CDB___db_err(dbp->dbenv, "illegal record number of 0");
01145 return (EINVAL);
01146 }
01147 return (0);
01148 }

Generated on Sun Jun 8 10:56:38 2008 for GNUmifluz by doxygen 1.5.5

AltStyle によって変換されたページ (->オリジナル) /