diff options
author | jbj <devnull@localhost> | 2001-03-21 18:33:35 +0000 |
---|---|---|
committer | jbj <devnull@localhost> | 2001-03-21 18:33:35 +0000 |
commit | 731946f4b90eb1173452dd30f1296dd825155d82 (patch) | |
tree | 67535f54ecb7e5463c06e62044e4efd84ae0291d /db/qam | |
parent | 7ed904da030dc4640ff9bce8458ba07cc09d830d (diff) | |
download | rpm-731946f4b90eb1173452dd30f1296dd825155d82.tar.gz rpm-731946f4b90eb1173452dd30f1296dd825155d82.tar.bz2 rpm-731946f4b90eb1173452dd30f1296dd825155d82.zip |
Initial revision
CVS patchset: 4644
CVS date: 2001/03/21 18:33:35
Diffstat (limited to 'db/qam')
-rw-r--r-- | db/qam/qam.c | 1357 | ||||
-rw-r--r-- | db/qam/qam.src | 124 | ||||
-rw-r--r-- | db/qam/qam_auto.c | 1282 | ||||
-rw-r--r-- | db/qam/qam_conv.c | 83 | ||||
-rw-r--r-- | db/qam/qam_files.c | 503 | ||||
-rw-r--r-- | db/qam/qam_method.c | 472 | ||||
-rw-r--r-- | db/qam/qam_open.c | 268 | ||||
-rw-r--r-- | db/qam/qam_rec.c | 732 | ||||
-rw-r--r-- | db/qam/qam_stat.c | 201 | ||||
-rw-r--r-- | db/qam/qam_upgrade.c | 111 | ||||
-rw-r--r-- | db/qam/qam_verify.c | 194 |
11 files changed, 5327 insertions, 0 deletions
diff --git a/db/qam/qam.c b/db/qam/qam.c new file mode 100644 index 000000000..0c9f45304 --- /dev/null +++ b/db/qam/qam.c @@ -0,0 +1,1357 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam.c,v 11.72 2001/01/16 20:10:55 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_shash.h" +#include "db_am.h" +#include "mp.h" +#include "lock.h" +#include "log.h" +#include "btree.h" +#include "qam.h" + +static int __qam_c_close __P((DBC *, db_pgno_t, int *)); +static int __qam_c_del __P((DBC *)); +static int __qam_c_destroy __P((DBC *)); +static int __qam_c_get __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *)); +static int __qam_c_put __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *)); +static int __qam_getno __P((DB *, const DBT *, db_recno_t *)); + +/* + * __qam_position -- + * Position a queued access method cursor at a record. This returns + * the page locked. *exactp will be set if the record is valid. + * PUBLIC: int __qam_position + * PUBLIC: __P((DBC *, db_recno_t *, qam_position_mode, int *)); + */ +int +__qam_position(dbc, recnop, mode, exactp) + DBC *dbc; /* open cursor */ + db_recno_t *recnop; /* pointer to recno to find */ + qam_position_mode mode;/* locking: read or write */ + int *exactp; /* indicate if it was found */ +{ + QUEUE_CURSOR *cp; + DB *dbp; + QAMDATA *qp; + db_pgno_t pg; + int ret; + + dbp = dbc->dbp; + cp = (QUEUE_CURSOR *)dbc->internal; + + /* Fetch the page for this recno. */ + pg = QAM_RECNO_PAGE(dbp, *recnop); + + if ((ret = __db_lget(dbc, 0, pg, mode == QAM_READ ? + DB_LOCK_READ : DB_LOCK_WRITE, 0, &cp->lock)) != 0) + return (ret); + cp->page = NULL; + *exactp = 0; + if ((ret = __qam_fget(dbp, &pg, + mode == QAM_WRITE ? DB_MPOOL_CREATE : 0, + &cp->page)) != 0) { + /* We did not fetch it, we can release the lock. */ + (void)__LPUT(dbc, cp->lock); + cp->lock.off = LOCK_INVALID; + if (mode != QAM_WRITE && (ret == EINVAL || ret == ENOENT)) + return (0); + return (ret); + } + cp->pgno = pg; + cp->indx = QAM_RECNO_INDEX(dbp, pg, *recnop); + + if (PGNO(cp->page) == 0) { + if (F_ISSET(dbp, DB_AM_RDONLY)) { + *exactp = 0; + return (0); + } + PGNO(cp->page) = pg; + TYPE(cp->page) = P_QAMDATA; + } + + qp = QAM_GET_RECORD(dbp, cp->page, cp->indx); + *exactp = F_ISSET(qp, QAM_VALID); + + return (ret); +} + +/* + * __qam_pitem -- + * Put an item on a queue page. Copy the data to the page and set the + * VALID and SET bits. If logging and the record was previously set, + * log that data, otherwise just log the new data. + * + * pagep must be write locked + * + * PUBLIC: int __qam_pitem + * PUBLIC: __P((DBC *, QPAGE *, u_int32_t, db_recno_t, DBT *)); + */ +int +__qam_pitem(dbc, pagep, indx, recno, data) + DBC *dbc; + QPAGE *pagep; + u_int32_t indx; + db_recno_t recno; + DBT *data; +{ + DB *dbp; + DBT olddata, pdata, *datap; + QAMDATA *qp; + QUEUE *t; + u_int32_t size; + u_int8_t *dest, *p; + int alloced, ret; + + alloced = ret = 0; + + dbp = dbc->dbp; + t = (QUEUE *)dbp->q_internal; + + if (data->size > t->re_len) + goto len_err; + + qp = QAM_GET_RECORD(dbp, pagep, indx); + + p = qp->data; + size = data->size; + datap = data; + if (F_ISSET(data, DB_DBT_PARTIAL)) { + if (data->doff + data->dlen > t->re_len) { + alloced = data->dlen; + goto len_err; + } + if (data->size != data->dlen) { +len_err: __db_err(dbp->dbenv, + "Length improper for fixed length record %lu", + (u_long)(alloced ? alloced : data->size)); + return (EINVAL); + } + if (data->size == t->re_len) + goto no_partial; + + /* + * If we are logging, then we have to build the record + * first, otherwise, we can simply drop the change + * directly on the page. After this clause, make + * sure that datap and p are set up correctly so that + * copying datap into p does the right thing. + * + * Note, I am changing this so that if the existing + * record is not valid, we create a complete record + * to log so that both this and the recovery code is simpler. + */ + + if (DB_LOGGING(dbc) || !F_ISSET(qp, QAM_VALID)) { + datap = &pdata; + memset(datap, 0, sizeof(*datap)); + + if ((ret = __os_malloc(dbp->dbenv, + t->re_len, NULL, &datap->data)) != 0) + return (ret); + alloced = 1; + datap->size = t->re_len; + + /* + * Construct the record if it's valid, otherwise set it + * all to the pad character. + */ + dest = datap->data; + if (F_ISSET(qp, QAM_VALID)) + memcpy(dest, p, t->re_len); + else + memset(dest, t->re_pad, t->re_len); + + dest += data->doff; + memcpy(dest, data->data, data->size); + } else { + datap = data; + p += data->doff; + } + } + +no_partial: + if (DB_LOGGING(dbc)) { + olddata.size = 0; + if (F_ISSET(qp, QAM_SET)) { + olddata.data = qp->data; + olddata.size = t->re_len; + } + if ((ret = __qam_add_log(dbp->dbenv, dbc->txn, &LSN(pagep), + 0, dbp->log_fileid, &LSN(pagep), pagep->pgno, + indx, recno, datap, qp->flags, + olddata.size == 0 ? NULL : &olddata)) != 0) + goto err; + } + + F_SET(qp, QAM_VALID | QAM_SET); + memcpy(p, datap->data, datap->size); + if (!F_ISSET(data, DB_DBT_PARTIAL)) + memset(p + datap->size, t->re_pad, t->re_len - datap->size); + +err: if (alloced) + __os_free(datap->data, t->re_len); + + return (ret); +} +/* + * __qam_c_put + * Cursor put for queued access method. + * BEFORE and AFTER cannot be specified. + */ +static int +__qam_c_put(dbc, key, data, flags, pgnop) + DBC *dbc; + DBT *key, *data; + u_int32_t flags; + db_pgno_t *pgnop; +{ + QUEUE_CURSOR *cp; + DB *dbp; + DB_LOCK lock; + QMETA *meta; + db_pgno_t pg; + db_recno_t new_cur, new_first; + u_int32_t opcode; + int exact, ret, t_ret; + + COMPQUIET(key, NULL); + + dbp = dbc->dbp; + if (pgnop != NULL) + *pgnop = PGNO_INVALID; + + cp = (QUEUE_CURSOR *)dbc->internal; + + /* Write lock the record. */ + if ((ret = __db_lget(dbc, + 0, cp->recno, DB_LOCK_WRITE, DB_LOCK_RECORD, &lock)) != 0) + return (ret); + + if ((ret = __qam_position(dbc, + &cp->recno, QAM_WRITE, &exact)) != 0) { + /* We could not get the page, we can release the record lock. */ + __LPUT(dbc, lock); + return (ret); + } + + if (exact && flags == DB_NOOVERWRITE) { + ret = __TLPUT(dbc, lock); + /* Doing record locking, release the page lock */ + if ((t_ret = __LPUT(dbc, cp->lock)) == 0) + cp->lock.off = LOCK_INVALID; + else + if (ret == 0) + ret = t_ret; + if ((t_ret = + __qam_fput(dbp, cp->pgno, cp->page, 0)) != 0 && ret == 0) + ret = t_ret; + cp->page = NULL; + return (ret == 0 ? DB_KEYEXIST : ret); + } + + /* Put the item on the page. */ + ret = __qam_pitem(dbc, (QPAGE *)cp->page, cp->indx, cp->recno, data); + + /* Doing record locking, release the page lock */ + if ((t_ret = __LPUT(dbc, cp->lock)) != 0 && ret == 0) + ret = t_ret; + if ((t_ret = + __qam_fput(dbp, cp->pgno, cp->page, DB_MPOOL_DIRTY)) && ret == 0) + ret = t_ret; + cp->page = NULL; + cp->lock = lock; + cp->lock_mode = DB_LOCK_WRITE; + if (ret != 0) + return (ret); + + /* We may need to reset the head or tail of the queue. */ + pg = ((QUEUE *)dbp->q_internal)->q_meta; + if ((ret = __db_lget(dbc, 0, pg, DB_LOCK_WRITE, 0, &lock)) != 0) + return (ret); + if ((ret = memp_fget(dbp->mpf, &pg, 0, &meta)) != 0) { + /* We did not fetch it, we can release the lock. */ + (void)__LPUT(dbc, lock); + return (ret); + } + + opcode = 0; + new_cur = new_first = 0; + + /* + * If the put address is outside the queue, adjust the head and + * tail of the queue. If the order is inverted we move + * the one which is closer. The first case is when the + * queue is empty, move first and current to where the new + * insert is. + */ + + if (meta->first_recno == meta->cur_recno) { + new_first = cp->recno; + new_cur = cp->recno + 1; + if (new_cur == RECNO_OOB) + new_cur++; + opcode |= QAM_SETFIRST; + opcode |= QAM_SETCUR; + } else { + if (QAM_BEFORE_FIRST(meta, cp->recno) && + (meta->first_recno <= meta->cur_recno || + meta->first_recno - cp->recno < cp->recno - meta->cur_recno)) { + new_first = cp->recno; + opcode |= QAM_SETFIRST; + } + + if (meta->cur_recno == cp->recno || + (QAM_AFTER_CURRENT(meta, cp->recno) && + (meta->first_recno <= meta->cur_recno || + cp->recno - meta->cur_recno <= meta->first_recno - cp->recno))) { + new_cur = cp->recno + 1; + if (new_cur == RECNO_OOB) + new_cur++; + opcode |= QAM_SETCUR; + } + } + + if (opcode != 0 && DB_LOGGING(dbc)) { + ret = __qam_mvptr_log(dbp->dbenv, dbc->txn, &meta->dbmeta.lsn, + 0, opcode, dbp->log_fileid, meta->first_recno, new_first, + meta->cur_recno, new_cur, &meta->dbmeta.lsn); + } + + if (opcode & QAM_SETCUR) + meta->cur_recno = new_cur; + if (opcode & QAM_SETFIRST) + meta->first_recno = new_first; + + if ((t_ret = + memp_fput(dbp->mpf, meta, opcode != 0 ? DB_MPOOL_DIRTY : 0)) != 0 && + ret == 0) + ret = t_ret; + + /* Don't hold the meta page long term. */ + if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0) + ret = t_ret; + return (ret); +} + +/* + * __qam_put -- + * Add a record to the queue. + * If we are doing anything but appending, just call qam_c_put to do the + * work. Otherwise we fast path things here. + * + * PUBLIC: int __qam_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t)); + */ +int +__qam_put(dbp, txn, key, data, flags) + DB *dbp; + DB_TXN *txn; + DBT *key, *data; + u_int32_t flags; +{ + QUEUE_CURSOR *cp; + DBC *dbc; + DB_LOCK lock; + QMETA *meta; + QPAGE *page; + QUEUE *qp; + db_pgno_t pg; + db_recno_t recno; + int ret, t_ret; + + PANIC_CHECK(dbp->dbenv); + DB_CHECK_TXN(dbp, txn); + + /* Allocate a cursor. */ + if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0) + return (ret); + + DEBUG_LWRITE(dbc, dbc->txn, "qam_put", key, data, flags); + + cp = (QUEUE_CURSOR *)dbc->internal; + + /* Check for invalid flags. */ + if ((ret = __db_putchk(dbp, + key, data, flags, F_ISSET(dbp, DB_AM_RDONLY), 0)) != 0) + goto done; + + /* If not appending, then just call the cursor routine */ + if (flags != DB_APPEND) { + if ((ret = __qam_getno(dbp, key, &cp->recno)) != 0) + goto done; + + ret = __qam_c_put(dbc, NULL, data, flags, NULL); + goto done; + } + + /* Write lock the meta page. */ + pg = ((QUEUE *)dbp->q_internal)->q_meta; + if ((ret = __db_lget(dbc, 0, pg, DB_LOCK_WRITE, 0, &lock)) != 0) + goto done; + if ((ret = memp_fget(dbp->mpf, &pg, 0, &meta)) != 0) { + /* We did not fetch it, we can release the lock. */ + (void)__LPUT(dbc, lock); + goto done; + } + + /* Record that we are going to allocate a record. */ + if (DB_LOGGING(dbc)) { + __qam_inc_log(dbp->dbenv, + dbc->txn, &meta->dbmeta.lsn, + 0, dbp->log_fileid, &meta->dbmeta.lsn); + } + + /* Get the next record number. */ + recno = meta->cur_recno; + meta->cur_recno++; + if (meta->cur_recno == RECNO_OOB) + meta->cur_recno++; + if (meta->cur_recno == meta->first_recno) { + meta->cur_recno--; + if (meta->cur_recno == RECNO_OOB) + meta->cur_recno--; + (void)__LPUT(dbc, lock); + ret = EFBIG; + goto err; + } + + if (QAM_BEFORE_FIRST(meta, recno)) + meta->first_recno = recno; + + /* Lock the record and release meta page lock. */ + if ((ret = __db_lget(dbc, + 1, recno, DB_LOCK_WRITE, DB_LOCK_RECORD, &lock)) != 0) + goto err; + + /* + * The application may modify the data based on the selected record + * number. + */ + if (flags == DB_APPEND && dbc->dbp->db_append_recno != NULL && + (ret = dbc->dbp->db_append_recno(dbc->dbp, data, recno)) != 0) { + (void)__LPUT(dbc, lock); + goto err; + } + + cp->lock = lock; + cp->lock_mode = DB_LOCK_WRITE; + + pg = QAM_RECNO_PAGE(dbp, recno); + + /* Fetch and write lock the data page. */ + if ((ret = __db_lget(dbc, 0, pg, DB_LOCK_WRITE, 0, &lock)) != 0) + goto err; + if ((ret = __qam_fget(dbp, &pg, DB_MPOOL_CREATE, &page)) != 0) { + /* We did not fetch it, we can release the lock. */ + (void)__LPUT(dbc, lock); + goto err; + } + + /* See if this is a new page. */ + if (page->pgno == 0) { + page->pgno = pg; + page->type = P_QAMDATA; + } + + /* Put the item on the page and log it. */ + ret = __qam_pitem(dbc, page, + QAM_RECNO_INDEX(dbp, pg, recno), recno, data); + + /* Doing record locking, release the page lock */ + if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0) + ret = t_ret; + + if ((t_ret + = __qam_fput(dbp, pg, page, DB_MPOOL_DIRTY)) != 0 && ret == 0) + ret = t_ret; + + /* Return the record number to the user. */ + if (ret == 0) + ret = __db_retcopy(dbp, key, + &recno, sizeof(recno), &dbc->rkey.data, &dbc->rkey.ulen); + + /* See if we are leaving the extent. */ + qp = (QUEUE *) dbp->q_internal; + if (qp->page_ext != 0 + && (recno % (qp->page_ext * qp->rec_page) == 0 + || recno == UINT32_T_MAX)) { + if ((ret = + __db_lget(dbc, 0, pg, DB_LOCK_WRITE, 0, &lock)) != 0) + goto err; + if (!QAM_AFTER_CURRENT(meta, recno)) + ret = __qam_fclose(dbp, pg); + (void)__LPUT(dbc, lock); + } + +err: + /* Release the meta page. */ + if ((t_ret + = memp_fput(dbp->mpf, meta, DB_MPOOL_DIRTY)) != 0 && ret == 0) + ret = t_ret; + +done: + /* Discard the cursor. */ + if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0) + ret = t_ret; + + return (ret); +} + +/* + * __qam_c_del -- + * Qam cursor->am_del function + */ +static int +__qam_c_del(dbc) + DBC *dbc; +{ + QUEUE_CURSOR *cp; + DB *dbp; + DBT data; + DB_LOCK lock; + PAGE *pagep; + QAMDATA *qp; + QMETA *meta; + db_pgno_t pg; + int exact, ret, t_ret; + + dbp = dbc->dbp; + cp = (QUEUE_CURSOR *)dbc->internal; + + pg = ((QUEUE *)dbp->q_internal)->q_meta; + if ((ret = __db_lget(dbc, 0, pg, DB_LOCK_READ, 0, &lock)) != 0) + return (ret); + if ((ret = memp_fget(dbp->mpf, &pg, 0, &meta)) != 0) { + /* We did not fetch it, we can release the lock. */ + (void)__LPUT(dbc, lock); + return (ret); + } + + if (QAM_NOT_VALID(meta, cp->recno)) + ret = DB_NOTFOUND; + + /* Don't hold the meta page long term. */ + if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0) + ret = t_ret; + if ((t_ret = memp_fput(dbp->mpf, meta, 0)) != 0 && ret == 0) + ret = t_ret; + + if (ret != 0) + return (ret); + + if ((ret = __db_lget(dbc, + 0, cp->recno, DB_LOCK_WRITE, DB_LOCK_RECORD, &lock)) != 0) + return (ret); + + cp->lock_mode = DB_LOCK_WRITE; + /* Find the record ; delete only deletes exact matches. */ + if ((ret = __qam_position(dbc, + &cp->recno, QAM_WRITE, &exact)) != 0) { + cp->lock = lock; + return (ret); + } + if (!exact) { + ret = DB_NOTFOUND; + goto err1; + } + + pagep = cp->page; + qp = QAM_GET_RECORD(dbp, pagep, cp->indx); + + if (DB_LOGGING(dbc)) { + if (((QUEUE *)dbp->q_internal)->page_ext == 0 + || ((QUEUE *)dbp->q_internal)->re_len == 0) { + if ((ret = + __qam_del_log(dbp->dbenv, + dbc->txn, &LSN(pagep), 0, + dbp->log_fileid, &LSN(pagep), + pagep->pgno, cp->indx, cp->recno)) != 0) + goto err1; + } else { + data.size = ((QUEUE *)dbp->q_internal)->re_len; + data.data = qp->data; + if ((ret = + __qam_delext_log(dbp->dbenv, dbc->txn, + &LSN(pagep), 0, dbp->log_fileid, &LSN(pagep), + pagep->pgno, cp->indx, cp->recno, &data)) != 0) + goto err1; + } + } + + F_CLR(qp, QAM_VALID); + +err1: + if ((t_ret = __qam_fput( + dbp, cp->pgno, cp->page, ret == 0 ? DB_MPOOL_DIRTY : 0)) != 0) + return (ret ? ret : t_ret); + cp->page = NULL; + /* Doing record locking, release the page lock */ + if ((t_ret = __LPUT(dbc, cp->lock)) != 0) { + cp->lock = lock; + return (ret ? ret : t_ret); + } + cp->lock = lock; + return (ret); +} + +/* + * __qam_delete -- + * Queue db->del function. + * + * PUBLIC: int __qam_delete __P((DB *, DB_TXN *, DBT *, u_int32_t)); + */ +int +__qam_delete(dbp, txn, key, flags) + DB *dbp; + DB_TXN *txn; + DBT *key; + u_int32_t flags; +{ + QUEUE_CURSOR *cp; + DBC *dbc; + int ret, t_ret; + + PANIC_CHECK(dbp->dbenv); + DB_CHECK_TXN(dbp, txn); + + /* Check for invalid flags. */ + if ((ret = + __db_delchk(dbp, key, flags, F_ISSET(dbp, DB_AM_RDONLY))) != 0) + return (ret); + + /* Acquire a cursor. */ + if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0) + return (ret); + + DEBUG_LWRITE(dbc, txn, "qam_delete", key, NULL, flags); + + cp = (QUEUE_CURSOR *)dbc->internal; + if ((ret = __qam_getno(dbp, key, &cp->recno)) != 0) + goto err; + + ret = __qam_c_del(dbc); + + /* Release the cursor. */ +err: if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0) + ret = t_ret; + + return (ret); +} + +#ifdef DEBUG_WOP +#define QDEBUG +#endif + +/* + * __qam_c_get -- + * Queue cursor->c_get function. + */ +static int +__qam_c_get(dbc, key, data, flags, pgnop) + DBC *dbc; + DBT *key, *data; + u_int32_t flags; + db_pgno_t *pgnop; +{ + DB *dbp; + DB_LOCK lock, pglock, metalock, save_lock; + DBT tmp; + PAGE *pg; + QAMDATA *qp; + QMETA *meta; + QUEUE *t; + QUEUE_CURSOR *cp; + db_indx_t save_indx; + db_lockmode_t lock_mode; + db_pgno_t metapno, save_page; + db_recno_t current, first, save_recno; + qam_position_mode mode; + u_int32_t rec_extent; + int exact, is_first, locked, ret, t_ret, wait, with_delete; + int put_mode, meta_dirty, retrying, skip_again, wrapped; + + cp = (QUEUE_CURSOR *)dbc->internal; + dbp = dbc->dbp; + + PANIC_CHECK(dbp->dbenv); + + wait = 0; + with_delete = 0; + retrying = 0; + rec_extent = 0; + lock_mode = DB_LOCK_READ; + mode = QAM_READ; + put_mode = 0; + t_ret = 0; + *pgnop = 0; + pg = NULL; + skip_again = 0; + + if (F_ISSET(dbc, DBC_RMW)) { + lock_mode = DB_LOCK_WRITE; + mode = QAM_WRITE; + } + + if (flags == DB_CONSUME_WAIT) { + wait = 1; + flags = DB_CONSUME; + } + if (flags == DB_CONSUME) { + DB_CHECK_TXN(dbp, dbc->txn); + with_delete = 1; + flags = DB_FIRST; + lock_mode = DB_LOCK_WRITE; + mode = QAM_CONSUME; + } + + DEBUG_LREAD(dbc, dbc->txn, "qam_c_get", + flags == DB_SET || flags == DB_SET_RANGE ? key : NULL, NULL, flags); + + is_first = 0; + + t = (QUEUE *)dbp->q_internal; + /* get the meta page */ + metapno = t->q_meta; + if ((ret = __db_lget(dbc, 0, metapno, lock_mode, 0, &metalock)) != 0) + return (ret); + locked = 1; + if ((ret = memp_fget(dbp->mpf, &metapno, 0, &meta)) != 0) { + /* We did not fetch it, we can release the lock. */ + (void)__LPUT(dbc, metalock); + return (ret); + } + + first = 0; + + /* Make lint and friends happy. */ + meta_dirty = 0; + + /* Release any previous lock if not in a transaction. */ + if (cp->lock.off != LOCK_INVALID) { + (void)__TLPUT(dbc, cp->lock); + cp->lock.off = LOCK_INVALID; + } + +retry: /* Update the record number. */ + switch (flags) { + case DB_CURRENT: + break; + case DB_NEXT_DUP: + ret = DB_NOTFOUND; + goto err; + /* NOTREACHED */ + case DB_NEXT: + case DB_NEXT_NODUP: + if (cp->recno != RECNO_OOB) { + ++cp->recno; + /* Wrap around, skipping zero. */ + if (cp->recno == RECNO_OOB) + cp->recno++; + break; + } + /* FALLTHROUGH */ + case DB_FIRST: + flags = DB_NEXT; + is_first = 1; + + /* get the first record number */ + cp->recno = first = meta->first_recno; + + break; + case DB_PREV: + case DB_PREV_NODUP: + if (cp->recno != RECNO_OOB) { + if (QAM_BEFORE_FIRST(meta, cp->recno) + || cp->recno == meta->first_recno) { + ret = DB_NOTFOUND; + goto err; + } + --cp->recno; + /* Wrap around, skipping zero. */ + if (cp->recno == RECNO_OOB) + --cp->recno; + break; + } + /* FALLTHROUGH */ + case DB_LAST: + if (meta->first_recno == meta->cur_recno) { + ret = DB_NOTFOUND; + goto err; + } + cp->recno = meta->cur_recno - 1; + if (cp->recno == RECNO_OOB) + cp->recno--; + break; + case DB_GET_BOTH: + case DB_SET: + case DB_SET_RANGE: + if ((ret = __qam_getno(dbp, key, &cp->recno)) != 0) + goto err; + break; + default: + ret = __db_unknown_flag(dbp->dbenv, "__qam_c_get", flags); + goto err; + } + + /* + * Check to see if we are out of data. Current points to + * the first free slot. + */ + if (cp->recno == meta->cur_recno || + QAM_AFTER_CURRENT(meta, cp->recno)) { + ret = DB_NOTFOUND; + pg = NULL; + if (wait) { + flags = DB_FIRST; + /* + * If first is not set, then we skipped a + * locked record, go back and find it. + * If we find a locked record again + * wait for it. + */ + if (first == 0) { + retrying = 1; + goto retry; + } + if (CDB_LOCKING(dbp->dbenv)) { + if ((ret = lock_get(dbp->dbenv, dbc->locker, + DB_LOCK_SWITCH, &dbc->lock_dbt, + DB_LOCK_WAIT, &dbc->mylock)) != 0) + goto err; + if ((ret = lock_get(dbp->dbenv, dbc->locker, + DB_LOCK_UPGRADE, &dbc->lock_dbt, DB_LOCK_WRITE, + &dbc->mylock)) != 0) + goto err; + goto retry; + } + /* + * Wait for someone to update the meta page. + * This will probably mean there is something + * in the queue. We then go back up and + * try again. + */ + if (locked == 0) { + if ((ret = __db_lget( dbc, + 0, metapno, lock_mode, 0, &metalock)) != 0) + goto err; + locked = 1; + if (cp->recno != RECNO_OOB && + !QAM_AFTER_CURRENT(meta, cp->recno)) + goto retry; + } + if ((ret = __db_lget(dbc, 0, metapno, + DB_LOCK_WAIT, DB_LOCK_SWITCH, &metalock)) != 0) + goto err; + if ((ret = lock_get(dbp->dbenv, dbc->locker, + DB_LOCK_UPGRADE, &dbc->lock_dbt, DB_LOCK_WRITE, + &metalock)) != 0) + goto err; + locked = 1; + goto retry; + } + + goto err; + } + + /* Don't hold the meta page long term. */ + if (locked) { + if ((ret = __LPUT(dbc, metalock)) != 0) + goto err; + locked = 0; + } + + /* Lock the record. */ + if ((ret = __db_lget(dbc, 0, cp->recno, lock_mode, + (with_delete && !retrying) ? + DB_LOCK_NOWAIT | DB_LOCK_RECORD : DB_LOCK_RECORD, + &lock)) == DB_LOCK_NOTGRANTED && with_delete) { +#ifdef QDEBUG + __db_logmsg(dbp->dbenv, + dbc->txn, "Queue S", 0, "%x %d %d %d", + dbc->locker, cp->recno, first, meta->first_recno); +#endif + first = 0; + goto retry; + } + + if (ret != 0) + goto err; + + /* + * In the DB_FIRST or DB_LAST cases we must wait and then start over + * since the first/last may have moved while we slept. + * We release our locks and try again. + */ + if ((!with_delete && is_first) || flags == DB_LAST) { + if ((ret = + __db_lget(dbc, 0, metapno, lock_mode, 0, &metalock)) != 0) + goto err; + if (cp->recno != + (is_first ? meta->first_recno : (meta->cur_recno - 1))) { + __LPUT(dbc, lock); + if (is_first) + flags = DB_FIRST; + locked = 1; + goto retry; + } + /* Don't hold the meta page long term. */ + if ((ret = __LPUT(dbc, metalock)) != 0) + goto err; + } + + /* Position the cursor on the record. */ + if ((ret = __qam_position(dbc, &cp->recno, mode, &exact)) != 0) { + /* We cannot get the page, release the record lock. */ + (void)__LPUT(dbc, lock); + goto err; + } + + pg = cp->page; + pglock = cp->lock; + cp->lock = lock; + cp->lock_mode = lock_mode; + + if (!exact) { + if (flags == DB_NEXT || flags == DB_NEXT_NODUP + || flags == DB_PREV || flags == DB_PREV_NODUP + || flags == DB_LAST) { + /* Release locks and try again. */ + if (pg != NULL) + (void)__qam_fput(dbp, cp->pgno, pg, 0); + cp->page = pg = NULL; + (void)__LPUT(dbc, pglock); + (void)__LPUT(dbc, cp->lock); + if (flags == DB_LAST) + flags = DB_PREV; + if (!with_delete) + is_first = 0; + retrying = 0; + goto retry; + } + /* this is for the SET and SET_RANGE cases */ + ret = DB_KEYEMPTY; + goto err1; + } + + /* Return the key if the user didn't give us one. */ + if (key != NULL && flags != DB_SET && flags != DB_GET_BOTH && + (ret = __db_retcopy(dbp, key, &cp->recno, sizeof(cp->recno), + &dbc->rkey.data, &dbc->rkey.ulen)) != 0) + goto err1; + + if (key != NULL) + F_SET(key, DB_DBT_ISSET); + + qp = QAM_GET_RECORD(dbp, pg, cp->indx); + + /* Return the data item. */ + if (flags == DB_GET_BOTH) { + /* + * Need to compare + */ + tmp.data = qp->data; + tmp.size = t->re_len; + if ((ret = __bam_defcmp(dbp, data, &tmp)) != 0) { + ret = DB_NOTFOUND; + goto err1; + } + } + if (data != NULL && (ret = __db_retcopy(dbp, data, + qp->data, t->re_len, &dbc->rdata.data, &dbc->rdata.ulen)) != 0) + goto err1; + + if (data != NULL) + F_SET(data, DB_DBT_ISSET); + + /* Finally, if we are doing DB_CONSUME mark the record. */ + if (with_delete) { + if (DB_LOGGING(dbc)) { + if (t->page_ext == 0 || t->re_len == 0) { + if ((ret = __qam_del_log(dbp->dbenv, dbc->txn, + &LSN(pg), 0, dbp->log_fileid, &LSN(pg), + pg->pgno, cp->indx, cp->recno)) != 0) + goto err1; + } else { + tmp.data = qp->data; + tmp.size = t->re_len; + if ((ret = + __qam_delext_log(dbp->dbenv, dbc->txn, + &LSN(pg), 0, dbp->log_fileid, &LSN(pg), + pg->pgno, cp->indx, cp->recno, &tmp)) != 0) + goto err1; + } + } + + F_CLR(qp, QAM_VALID); + put_mode = DB_MPOOL_DIRTY; + + if ((ret = __LPUT(dbc, pglock)) != 0) + goto err; + + /* + * Now we need to update the metapage + * first pointer. If we have deleted + * the record that is pointed to by + * first_recno then we move it as far + * forward as we can without blocking. + * The metapage lock must be held for + * the whole scan otherwise someone could + * do a random insert behind where we are + * looking. + */ + + if (locked == 0 && (ret = __db_lget( + dbc, 0, metapno, lock_mode, 0, &metalock)) != 0) + goto err1; + locked = 1; +#ifdef QDEBUG + __db_logmsg(dbp->dbenv, + dbc->txn, "Queue D", 0, "%x %d %d %d", + dbc->locker, cp->recno, first, meta->first_recno); +#endif + /* + * See if we deleted the "first" record. If + * first is zero then we skipped something, + * see if first_recno has been move passed + * that to the record that we deleted. + */ + if (first == 0) + first = cp->recno; + if (first != meta->first_recno) + goto done; + + save_page = cp->pgno; + save_indx = cp->indx; + save_recno = cp->recno; + save_lock = cp->lock; + + /* + * If we skipped some deleted records, we need to + * reposition on the first one. Get a lock + * in case someone is trying to put it back. + */ + if (first != cp->recno) { + ret = __db_lget(dbc, 0, first, DB_LOCK_READ, + DB_LOCK_NOWAIT | DB_LOCK_RECORD, &lock); + if (ret == DB_LOCK_NOTGRANTED) { + ret = 0; + goto done; + } + if (ret != 0) + goto err1; + if ((ret = + __qam_fput(dbp, cp->pgno, cp->page, put_mode)) != 0) + goto err1; + cp->page = NULL; + put_mode = 0; + if ((ret = __qam_position(dbc, + &first, QAM_READ, &exact)) != 0 || exact != 0) { + (void)__LPUT(dbc, lock); + goto err1; + } + if ((ret =__LPUT(dbc, lock)) != 0) + goto err1; + if ((ret = __LPUT(dbc, cp->lock)) != 0) + goto err1; + } + + current = meta->cur_recno; + wrapped = 0; + if (first > current) + wrapped = 1; + rec_extent = meta->page_ext * meta->rec_page; + + /* Loop until we find a record or hit current */ + for (;;) { + /* + * Check to see if we are moving off the extent + * and remove the extent. + * If we are moving off a page we need to + * get rid of the buffer. + * Wait for the lagging readers to move off the + * page. + */ + if (rec_extent != 0 + && ((exact = first % rec_extent == 0) + || first % meta->rec_page == 0 + || first == UINT32_T_MAX)) { + if (exact == 1 && (ret = __db_lget(dbc, + 0, cp->pgno, DB_LOCK_WRITE, 0, &cp->lock)) != 0) + break; + +#ifdef QDEBUG + __db_logmsg(dbp->dbenv, + dbc->txn, "Queue R", 0, "%x %d %d %d", + dbc->locker, cp->pgno, first, meta->first_recno); +#endif + put_mode |= DB_MPOOL_DISCARD; + if ((ret = __qam_fput(dbp, + cp->pgno, cp->page, put_mode)) != 0) + break; + cp->page = NULL; + + if (exact == 1) { + ret = __qam_fremove(dbp, cp->pgno); + t_ret = __LPUT(dbc, cp->lock); + } + if (ret != 0) + break; + if (t_ret != 0) { + ret = t_ret; + break; + } + } else if ((ret = + __qam_fput(dbp, cp->pgno, cp->page, put_mode)) != 0) + break; + cp->page = NULL; + first++; + if (first == RECNO_OOB) { + wrapped = 0; + first++; + } + + /* + * LOOP EXIT when we come move to the current + * pointer. + */ + if (!wrapped && first >= current) + break; + + ret = __db_lget(dbc, 0, first, DB_LOCK_READ, + DB_LOCK_NOWAIT | DB_LOCK_RECORD, &lock); + if (ret == DB_LOCK_NOTGRANTED) { + ret = 0; + break; + } + if (ret != 0) + break; + + if ((ret = __qam_position(dbc, + &first, QAM_READ, &exact)) != 0) { + (void)__LPUT(dbc, lock); + break; + } + put_mode = 0; + if ((ret =__LPUT(dbc, lock)) != 0 + || (ret = __LPUT(dbc, cp->lock)) != 0 ||exact) { + if ((t_ret = __qam_fput(dbp, cp->pgno, + cp->page, put_mode)) != 0 && ret == 0) + ret = t_ret; + cp->page = NULL; + break; + } + } + + cp->pgno = save_page; + cp->indx = save_indx; + cp->recno = save_recno; + cp->lock = save_lock; + + /* + * We have advanced as far as we can. + * Advance first_recno to this point. + */ + if (meta->first_recno != first) { +#ifdef QDEBUG + __db_logmsg(dbp->dbenv, dbc->txn, "Queue M", + 0, "%x %d %d %d", dbc->locker, cp->recno, + first, meta->first_recno); +#endif + if (DB_LOGGING(dbc)) + if ((ret = + __qam_incfirst_log(dbp->dbenv, + dbc->txn, &meta->dbmeta.lsn, 0, + dbp->log_fileid, cp->recno)) != 0) + goto err; + meta->first_recno = first; + meta_dirty = 1; + } + } + +done: +err1: if (cp->page != NULL) { + t_ret = __qam_fput(dbp, cp->pgno, cp->page, put_mode); + + if (!ret) + ret = t_ret; + /* Doing record locking, release the page lock */ + t_ret = __LPUT(dbc, pglock); + cp->page = NULL; + } + +err: if (!ret) + ret = t_ret; + if (meta) { + + /* release the meta page */ + t_ret = memp_fput( + dbp->mpf, meta, meta_dirty ? DB_MPOOL_DIRTY : 0); + + if (!ret) + ret = t_ret; + + /* Don't hold the meta page long term. */ + if (locked) + t_ret = __LPUT(dbc, metalock); + } + DB_ASSERT(metalock.off == LOCK_INVALID); + + /* + * There is no need to keep the record locked if we are + * not in a transaction. + */ + if (t_ret == 0) + t_ret = __TLPUT(dbc, cp->lock); + + return (ret ? ret : t_ret); +} + +/* + * __qam_c_close -- + * Close down the cursor from a single use. + */ +static int +__qam_c_close(dbc, root_pgno, rmroot) + DBC *dbc; + db_pgno_t root_pgno; + int *rmroot; +{ + QUEUE_CURSOR *cp; + + COMPQUIET(root_pgno, 0); + COMPQUIET(rmroot, NULL); + + cp = (QUEUE_CURSOR *)dbc->internal; + + /* Discard any locks not acquired inside of a transaction. */ + if (cp->lock.off != LOCK_INVALID) { + (void)__TLPUT(dbc, cp->lock); + cp->lock.off = LOCK_INVALID; + } + + cp->page = NULL; + cp->pgno = PGNO_INVALID; + cp->indx = 0; + cp->lock.off = LOCK_INVALID; + cp->lock_mode = DB_LOCK_NG; + cp->recno = RECNO_OOB; + cp->flags = 0; + + return (0); +} + +/* + * __qam_c_dup -- + * Duplicate a queue cursor, such that the new one holds appropriate + * locks for the position of the original. + * + * PUBLIC: int __qam_c_dup __P((DBC *, DBC *)); + */ +int +__qam_c_dup(orig_dbc, new_dbc) + DBC *orig_dbc, *new_dbc; +{ + QUEUE_CURSOR *orig, *new; + + orig = (QUEUE_CURSOR *)orig_dbc->internal; + new = (QUEUE_CURSOR *)new_dbc->internal; + + new->recno = orig->recno; + + /* reget the long term lock if we are not in a xact */ + if (orig_dbc->txn != NULL || + !STD_LOCKING(orig_dbc) || orig->lock.off == LOCK_INVALID) + return (0); + + return (__db_lget(new_dbc, + 0, new->recno, new->lock_mode, DB_LOCK_RECORD, &new->lock)); +} + +/* + * __qam_c_init + * + * PUBLIC: int __qam_c_init __P((DBC *)); + */ +int +__qam_c_init(dbc) + DBC *dbc; +{ + QUEUE_CURSOR *cp; + DB *dbp; + int ret; + + dbp = dbc->dbp; + + /* Allocate the internal structure. */ + cp = (QUEUE_CURSOR *)dbc->internal; + if (cp == NULL) { + if ((ret = + __os_calloc(dbp->dbenv, 1, sizeof(QUEUE_CURSOR), &cp)) != 0) + return (ret); + dbc->internal = (DBC_INTERNAL *)cp; + } + + /* Initialize methods. */ + dbc->c_close = __db_c_close; + dbc->c_count = __db_c_count; + dbc->c_del = __db_c_del; + dbc->c_dup = __db_c_dup; + dbc->c_get = __db_c_get; + dbc->c_put = __db_c_put; + dbc->c_am_close = __qam_c_close; + dbc->c_am_del = __qam_c_del; + dbc->c_am_destroy = __qam_c_destroy; + dbc->c_am_get = __qam_c_get; + dbc->c_am_put = __qam_c_put; + dbc->c_am_writelock = NULL; + + return (0); +} + +/* + * __qam_c_destroy -- + * Close a single cursor -- internal version. + */ +static int +__qam_c_destroy(dbc) + DBC *dbc; +{ + /* Discard the structures. */ + __os_free(dbc->internal, sizeof(QUEUE_CURSOR)); + + return (0); +} + +/* + * __qam_getno -- + * Check the user's record number. + */ +static int +__qam_getno(dbp, key, rep) + DB *dbp; + const DBT *key; + db_recno_t *rep; +{ + if ((*rep = *(db_recno_t *)key->data) == 0) { + __db_err(dbp->dbenv, "illegal record number of 0"); + return (EINVAL); + } + return (0); +} diff --git a/db/qam/qam.src b/db/qam/qam.src new file mode 100644 index 000000000..507d7a652 --- /dev/null +++ b/db/qam/qam.src @@ -0,0 +1,124 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: qam.src,v 11.15 2001/01/16 20:10:55 ubell Exp $ + */ + +PREFIX qam + +INCLUDE #include "db_config.h" +INCLUDE +INCLUDE #ifndef NO_SYSTEM_INCLUDES +INCLUDE #include <sys/types.h> +INCLUDE +INCLUDE #include <ctype.h> +INCLUDE #include <errno.h> +INCLUDE #include <string.h> +INCLUDE #endif +INCLUDE +INCLUDE #include "db_int.h" +INCLUDE #include "db_page.h" +INCLUDE #include "db_dispatch.h" +INCLUDE #include "db_am.h" +INCLUDE #include "qam.h" +INCLUDE #include "txn.h" +INCLUDE + +/* + * inc + * Used when we increment a record number. These do not actually + * tell you what record number you got, just that you incremented + * the record number. These operations are never undone. + */ +BEGIN inc 76 +ARG fileid int32_t ld +POINTER lsn DB_LSN * lu +END + +/* + * incfirst + * Used when we increment first_recno. + */ +BEGIN incfirst 77 +ARG fileid int32_t ld +ARG recno db_recno_t lu +END + +/* + * mvptr + * Used when we change one or both of cur_recno and first_recno. + */ +BEGIN mvptr 78 +ARG opcode u_int32_t lu +ARG fileid int32_t ld +ARG old_first db_recno_t lu +ARG new_first db_recno_t lu +ARG old_cur db_recno_t lu +ARG new_cur db_recno_t lu +POINTER metalsn DB_LSN * lu +END + +/* + * del + * Used when we delete a record. + * recno is the record that is being deleted. + */ +BEGIN del 79 +ARG fileid int32_t ld +POINTER lsn DB_LSN * lu +ARG pgno db_pgno_t lu +ARG indx u_int32_t lu +ARG recno db_recno_t lu +END + +/* + * add + * Used when we put a record on a page. + * recno is the record being added. + * data is the record itself. + */ +BEGIN add 80 +ARG fileid int32_t ld +POINTER lsn DB_LSN * lu +ARG pgno db_pgno_t lu +ARG indx u_int32_t lu +ARG recno db_recno_t lu +DBT data DBT s +ARG vflag u_int32_t lu +DBT olddata DBT s +END + +/* + * delete + * Used when we remove a Queue extent file. + */ +BEGIN delete 81 +DBT name DBT s +POINTER lsn DB_LSN * lu +END + +/* + * rename + * Used when we rename a Queue extent file. + */ +BEGIN rename 82 +DBT name DBT s +DBT newname DBT s +END + +/* + * delext + * Used when we delete a record in extent based queue. + * recno is the record that is being deleted. + */ +BEGIN delext 83 +ARG fileid int32_t ld +POINTER lsn DB_LSN * lu +ARG pgno db_pgno_t lu +ARG indx u_int32_t lu +ARG recno db_recno_t lu +DBT data DBT s +END diff --git a/db/qam/qam_auto.c b/db/qam/qam_auto.c new file mode 100644 index 000000000..cfdba3195 --- /dev/null +++ b/db/qam/qam_auto.c @@ -0,0 +1,1282 @@ +/* Do not edit: automatically built by gen_rec.awk. */ +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_dispatch.h" +#include "db_am.h" +#include "qam.h" +#include "txn.h" + +int +__qam_inc_log(dbenv, txnid, ret_lsnp, flags, + fileid, lsn) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + int32_t fileid; + DB_LSN * lsn; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_qam_inc; + if (txnid != NULL && + TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + ZERO_LSN(null_lsn); + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(fileid) + + sizeof(*lsn); + if ((ret = __os_malloc(dbenv, logrec.size, NULL, &logrec.data)) != 0) + return (ret); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(bp, &fileid, sizeof(fileid)); + bp += sizeof(fileid); + if (lsn != NULL) + memcpy(bp, lsn, sizeof(*lsn)); + else + memset(bp, 0, sizeof(*lsn)); + bp += sizeof(*lsn); + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size); + ret = log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + __os_free(logrec.data, logrec.size); + return (ret); +} + +int +__qam_inc_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __qam_inc_args *argp; + u_int32_t i; + u_int ch; + int ret; + + i = 0; + ch = 0; + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __qam_inc_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]qam_inc: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tfileid: %ld\n", (long)argp->fileid); + printf("\tlsn: [%lu][%lu]\n", + (u_long)argp->lsn.file, (u_long)argp->lsn.offset); + printf("\n"); + __os_free(argp, 0); + return (0); +} + +int +__qam_inc_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __qam_inc_args **argpp; +{ + __qam_inc_args *argp; + u_int8_t *bp; + int ret; + + ret = __os_malloc(dbenv, sizeof(__qam_inc_args) + + sizeof(DB_TXN), NULL, &argp); + if (ret != 0) + return (ret); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(&argp->fileid, bp, sizeof(argp->fileid)); + bp += sizeof(argp->fileid); + memcpy(&argp->lsn, bp, sizeof(argp->lsn)); + bp += sizeof(argp->lsn); + *argpp = argp; + return (0); +} + +int +__qam_incfirst_log(dbenv, txnid, ret_lsnp, flags, + fileid, recno) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + int32_t fileid; + db_recno_t recno; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_qam_incfirst; + if (txnid != NULL && + TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + ZERO_LSN(null_lsn); + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(fileid) + + sizeof(recno); + if ((ret = __os_malloc(dbenv, logrec.size, NULL, &logrec.data)) != 0) + return (ret); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(bp, &fileid, sizeof(fileid)); + bp += sizeof(fileid); + memcpy(bp, &recno, sizeof(recno)); + bp += sizeof(recno); + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size); + ret = log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + __os_free(logrec.data, logrec.size); + return (ret); +} + +int +__qam_incfirst_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __qam_incfirst_args *argp; + u_int32_t i; + u_int ch; + int ret; + + i = 0; + ch = 0; + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __qam_incfirst_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]qam_incfirst: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tfileid: %ld\n", (long)argp->fileid); + printf("\trecno: %lu\n", (u_long)argp->recno); + printf("\n"); + __os_free(argp, 0); + return (0); +} + +int +__qam_incfirst_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __qam_incfirst_args **argpp; +{ + __qam_incfirst_args *argp; + u_int8_t *bp; + int ret; + + ret = __os_malloc(dbenv, sizeof(__qam_incfirst_args) + + sizeof(DB_TXN), NULL, &argp); + if (ret != 0) + return (ret); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(&argp->fileid, bp, sizeof(argp->fileid)); + bp += sizeof(argp->fileid); + memcpy(&argp->recno, bp, sizeof(argp->recno)); + bp += sizeof(argp->recno); + *argpp = argp; + return (0); +} + +int +__qam_mvptr_log(dbenv, txnid, ret_lsnp, flags, + opcode, fileid, old_first, new_first, old_cur, new_cur, + metalsn) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + u_int32_t opcode; + int32_t fileid; + db_recno_t old_first; + db_recno_t new_first; + db_recno_t old_cur; + db_recno_t new_cur; + DB_LSN * metalsn; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_qam_mvptr; + if (txnid != NULL && + TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + ZERO_LSN(null_lsn); + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(opcode) + + sizeof(fileid) + + sizeof(old_first) + + sizeof(new_first) + + sizeof(old_cur) + + sizeof(new_cur) + + sizeof(*metalsn); + if ((ret = __os_malloc(dbenv, logrec.size, NULL, &logrec.data)) != 0) + return (ret); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(bp, &opcode, sizeof(opcode)); + bp += sizeof(opcode); + memcpy(bp, &fileid, sizeof(fileid)); + bp += sizeof(fileid); + memcpy(bp, &old_first, sizeof(old_first)); + bp += sizeof(old_first); + memcpy(bp, &new_first, sizeof(new_first)); + bp += sizeof(new_first); + memcpy(bp, &old_cur, sizeof(old_cur)); + bp += sizeof(old_cur); + memcpy(bp, &new_cur, sizeof(new_cur)); + bp += sizeof(new_cur); + if (metalsn != NULL) + memcpy(bp, metalsn, sizeof(*metalsn)); + else + memset(bp, 0, sizeof(*metalsn)); + bp += sizeof(*metalsn); + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size); + ret = log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + __os_free(logrec.data, logrec.size); + return (ret); +} + +int +__qam_mvptr_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __qam_mvptr_args *argp; + u_int32_t i; + u_int ch; + int ret; + + i = 0; + ch = 0; + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __qam_mvptr_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]qam_mvptr: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\topcode: %lu\n", (u_long)argp->opcode); + printf("\tfileid: %ld\n", (long)argp->fileid); + printf("\told_first: %lu\n", (u_long)argp->old_first); + printf("\tnew_first: %lu\n", (u_long)argp->new_first); + printf("\told_cur: %lu\n", (u_long)argp->old_cur); + printf("\tnew_cur: %lu\n", (u_long)argp->new_cur); + printf("\tmetalsn: [%lu][%lu]\n", + (u_long)argp->metalsn.file, (u_long)argp->metalsn.offset); + printf("\n"); + __os_free(argp, 0); + return (0); +} + +int +__qam_mvptr_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __qam_mvptr_args **argpp; +{ + __qam_mvptr_args *argp; + u_int8_t *bp; + int ret; + + ret = __os_malloc(dbenv, sizeof(__qam_mvptr_args) + + sizeof(DB_TXN), NULL, &argp); + if (ret != 0) + return (ret); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(&argp->opcode, bp, sizeof(argp->opcode)); + bp += sizeof(argp->opcode); + memcpy(&argp->fileid, bp, sizeof(argp->fileid)); + bp += sizeof(argp->fileid); + memcpy(&argp->old_first, bp, sizeof(argp->old_first)); + bp += sizeof(argp->old_first); + memcpy(&argp->new_first, bp, sizeof(argp->new_first)); + bp += sizeof(argp->new_first); + memcpy(&argp->old_cur, bp, sizeof(argp->old_cur)); + bp += sizeof(argp->old_cur); + memcpy(&argp->new_cur, bp, sizeof(argp->new_cur)); + bp += sizeof(argp->new_cur); + memcpy(&argp->metalsn, bp, sizeof(argp->metalsn)); + bp += sizeof(argp->metalsn); + *argpp = argp; + return (0); +} + +int +__qam_del_log(dbenv, txnid, ret_lsnp, flags, + fileid, lsn, pgno, indx, recno) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + int32_t fileid; + DB_LSN * lsn; + db_pgno_t pgno; + u_int32_t indx; + db_recno_t recno; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_qam_del; + if (txnid != NULL && + TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + ZERO_LSN(null_lsn); + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(fileid) + + sizeof(*lsn) + + sizeof(pgno) + + sizeof(indx) + + sizeof(recno); + if ((ret = __os_malloc(dbenv, logrec.size, NULL, &logrec.data)) != 0) + return (ret); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(bp, &fileid, sizeof(fileid)); + bp += sizeof(fileid); + if (lsn != NULL) + memcpy(bp, lsn, sizeof(*lsn)); + else + memset(bp, 0, sizeof(*lsn)); + bp += sizeof(*lsn); + memcpy(bp, &pgno, sizeof(pgno)); + bp += sizeof(pgno); + memcpy(bp, &indx, sizeof(indx)); + bp += sizeof(indx); + memcpy(bp, &recno, sizeof(recno)); + bp += sizeof(recno); + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size); + ret = log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + __os_free(logrec.data, logrec.size); + return (ret); +} + +int +__qam_del_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __qam_del_args *argp; + u_int32_t i; + u_int ch; + int ret; + + i = 0; + ch = 0; + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __qam_del_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]qam_del: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tfileid: %ld\n", (long)argp->fileid); + printf("\tlsn: [%lu][%lu]\n", + (u_long)argp->lsn.file, (u_long)argp->lsn.offset); + printf("\tpgno: %lu\n", (u_long)argp->pgno); + printf("\tindx: %lu\n", (u_long)argp->indx); + printf("\trecno: %lu\n", (u_long)argp->recno); + printf("\n"); + __os_free(argp, 0); + return (0); +} + +int +__qam_del_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __qam_del_args **argpp; +{ + __qam_del_args *argp; + u_int8_t *bp; + int ret; + + ret = __os_malloc(dbenv, sizeof(__qam_del_args) + + sizeof(DB_TXN), NULL, &argp); + if (ret != 0) + return (ret); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(&argp->fileid, bp, sizeof(argp->fileid)); + bp += sizeof(argp->fileid); + memcpy(&argp->lsn, bp, sizeof(argp->lsn)); + bp += sizeof(argp->lsn); + memcpy(&argp->pgno, bp, sizeof(argp->pgno)); + bp += sizeof(argp->pgno); + memcpy(&argp->indx, bp, sizeof(argp->indx)); + bp += sizeof(argp->indx); + memcpy(&argp->recno, bp, sizeof(argp->recno)); + bp += sizeof(argp->recno); + *argpp = argp; + return (0); +} + +int +__qam_add_log(dbenv, txnid, ret_lsnp, flags, + fileid, lsn, pgno, indx, recno, data, + vflag, olddata) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + int32_t fileid; + DB_LSN * lsn; + db_pgno_t pgno; + u_int32_t indx; + db_recno_t recno; + const DBT *data; + u_int32_t vflag; + const DBT *olddata; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t zero; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_qam_add; + if (txnid != NULL && + TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + ZERO_LSN(null_lsn); + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(fileid) + + sizeof(*lsn) + + sizeof(pgno) + + sizeof(indx) + + sizeof(recno) + + sizeof(u_int32_t) + (data == NULL ? 0 : data->size) + + sizeof(vflag) + + sizeof(u_int32_t) + (olddata == NULL ? 0 : olddata->size); + if ((ret = __os_malloc(dbenv, logrec.size, NULL, &logrec.data)) != 0) + return (ret); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(bp, &fileid, sizeof(fileid)); + bp += sizeof(fileid); + if (lsn != NULL) + memcpy(bp, lsn, sizeof(*lsn)); + else + memset(bp, 0, sizeof(*lsn)); + bp += sizeof(*lsn); + memcpy(bp, &pgno, sizeof(pgno)); + bp += sizeof(pgno); + memcpy(bp, &indx, sizeof(indx)); + bp += sizeof(indx); + memcpy(bp, &recno, sizeof(recno)); + bp += sizeof(recno); + if (data == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &data->size, sizeof(data->size)); + bp += sizeof(data->size); + memcpy(bp, data->data, data->size); + bp += data->size; + } + memcpy(bp, &vflag, sizeof(vflag)); + bp += sizeof(vflag); + if (olddata == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &olddata->size, sizeof(olddata->size)); + bp += sizeof(olddata->size); + memcpy(bp, olddata->data, olddata->size); + bp += olddata->size; + } + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size); + ret = log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + __os_free(logrec.data, logrec.size); + return (ret); +} + +int +__qam_add_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __qam_add_args *argp; + u_int32_t i; + u_int ch; + int ret; + + i = 0; + ch = 0; + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __qam_add_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]qam_add: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tfileid: %ld\n", (long)argp->fileid); + printf("\tlsn: [%lu][%lu]\n", + (u_long)argp->lsn.file, (u_long)argp->lsn.offset); + printf("\tpgno: %lu\n", (u_long)argp->pgno); + printf("\tindx: %lu\n", (u_long)argp->indx); + printf("\trecno: %lu\n", (u_long)argp->recno); + printf("\tdata: "); + for (i = 0; i < argp->data.size; i++) { + ch = ((u_int8_t *)argp->data.data)[i]; + if (isprint(ch) || ch == 0xa) + putchar(ch); + else + printf("%#x ", ch); + } + printf("\n"); + printf("\tvflag: %lu\n", (u_long)argp->vflag); + printf("\tolddata: "); + for (i = 0; i < argp->olddata.size; i++) { + ch = ((u_int8_t *)argp->olddata.data)[i]; + if (isprint(ch) || ch == 0xa) + putchar(ch); + else + printf("%#x ", ch); + } + printf("\n"); + printf("\n"); + __os_free(argp, 0); + return (0); +} + +int +__qam_add_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __qam_add_args **argpp; +{ + __qam_add_args *argp; + u_int8_t *bp; + int ret; + + ret = __os_malloc(dbenv, sizeof(__qam_add_args) + + sizeof(DB_TXN), NULL, &argp); + if (ret != 0) + return (ret); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(&argp->fileid, bp, sizeof(argp->fileid)); + bp += sizeof(argp->fileid); + memcpy(&argp->lsn, bp, sizeof(argp->lsn)); + bp += sizeof(argp->lsn); + memcpy(&argp->pgno, bp, sizeof(argp->pgno)); + bp += sizeof(argp->pgno); + memcpy(&argp->indx, bp, sizeof(argp->indx)); + bp += sizeof(argp->indx); + memcpy(&argp->recno, bp, sizeof(argp->recno)); + bp += sizeof(argp->recno); + memset(&argp->data, 0, sizeof(argp->data)); + memcpy(&argp->data.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->data.data = bp; + bp += argp->data.size; + memcpy(&argp->vflag, bp, sizeof(argp->vflag)); + bp += sizeof(argp->vflag); + memset(&argp->olddata, 0, sizeof(argp->olddata)); + memcpy(&argp->olddata.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->olddata.data = bp; + bp += argp->olddata.size; + *argpp = argp; + return (0); +} + +int +__qam_delete_log(dbenv, txnid, ret_lsnp, flags, + name, lsn) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + const DBT *name; + DB_LSN * lsn; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t zero; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_qam_delete; + if (txnid != NULL && + TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + ZERO_LSN(null_lsn); + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(u_int32_t) + (name == NULL ? 0 : name->size) + + sizeof(*lsn); + if ((ret = __os_malloc(dbenv, logrec.size, NULL, &logrec.data)) != 0) + return (ret); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + if (name == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &name->size, sizeof(name->size)); + bp += sizeof(name->size); + memcpy(bp, name->data, name->size); + bp += name->size; + } + if (lsn != NULL) + memcpy(bp, lsn, sizeof(*lsn)); + else + memset(bp, 0, sizeof(*lsn)); + bp += sizeof(*lsn); + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size); + ret = log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + __os_free(logrec.data, logrec.size); + return (ret); +} + +int +__qam_delete_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __qam_delete_args *argp; + u_int32_t i; + u_int ch; + int ret; + + i = 0; + ch = 0; + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __qam_delete_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]qam_delete: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tname: "); + for (i = 0; i < argp->name.size; i++) { + ch = ((u_int8_t *)argp->name.data)[i]; + if (isprint(ch) || ch == 0xa) + putchar(ch); + else + printf("%#x ", ch); + } + printf("\n"); + printf("\tlsn: [%lu][%lu]\n", + (u_long)argp->lsn.file, (u_long)argp->lsn.offset); + printf("\n"); + __os_free(argp, 0); + return (0); +} + +int +__qam_delete_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __qam_delete_args **argpp; +{ + __qam_delete_args *argp; + u_int8_t *bp; + int ret; + + ret = __os_malloc(dbenv, sizeof(__qam_delete_args) + + sizeof(DB_TXN), NULL, &argp); + if (ret != 0) + return (ret); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memset(&argp->name, 0, sizeof(argp->name)); + memcpy(&argp->name.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->name.data = bp; + bp += argp->name.size; + memcpy(&argp->lsn, bp, sizeof(argp->lsn)); + bp += sizeof(argp->lsn); + *argpp = argp; + return (0); +} + +int +__qam_rename_log(dbenv, txnid, ret_lsnp, flags, + name, newname) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + const DBT *name; + const DBT *newname; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t zero; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_qam_rename; + if (txnid != NULL && + TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + ZERO_LSN(null_lsn); + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(u_int32_t) + (name == NULL ? 0 : name->size) + + sizeof(u_int32_t) + (newname == NULL ? 0 : newname->size); + if ((ret = __os_malloc(dbenv, logrec.size, NULL, &logrec.data)) != 0) + return (ret); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + if (name == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &name->size, sizeof(name->size)); + bp += sizeof(name->size); + memcpy(bp, name->data, name->size); + bp += name->size; + } + if (newname == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &newname->size, sizeof(newname->size)); + bp += sizeof(newname->size); + memcpy(bp, newname->data, newname->size); + bp += newname->size; + } + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size); + ret = log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + __os_free(logrec.data, logrec.size); + return (ret); +} + +int +__qam_rename_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __qam_rename_args *argp; + u_int32_t i; + u_int ch; + int ret; + + i = 0; + ch = 0; + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __qam_rename_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]qam_rename: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tname: "); + for (i = 0; i < argp->name.size; i++) { + ch = ((u_int8_t *)argp->name.data)[i]; + if (isprint(ch) || ch == 0xa) + putchar(ch); + else + printf("%#x ", ch); + } + printf("\n"); + printf("\tnewname: "); + for (i = 0; i < argp->newname.size; i++) { + ch = ((u_int8_t *)argp->newname.data)[i]; + if (isprint(ch) || ch == 0xa) + putchar(ch); + else + printf("%#x ", ch); + } + printf("\n"); + printf("\n"); + __os_free(argp, 0); + return (0); +} + +int +__qam_rename_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __qam_rename_args **argpp; +{ + __qam_rename_args *argp; + u_int8_t *bp; + int ret; + + ret = __os_malloc(dbenv, sizeof(__qam_rename_args) + + sizeof(DB_TXN), NULL, &argp); + if (ret != 0) + return (ret); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memset(&argp->name, 0, sizeof(argp->name)); + memcpy(&argp->name.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->name.data = bp; + bp += argp->name.size; + memset(&argp->newname, 0, sizeof(argp->newname)); + memcpy(&argp->newname.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->newname.data = bp; + bp += argp->newname.size; + *argpp = argp; + return (0); +} + +int +__qam_delext_log(dbenv, txnid, ret_lsnp, flags, + fileid, lsn, pgno, indx, recno, data) + DB_ENV *dbenv; + DB_TXN *txnid; + DB_LSN *ret_lsnp; + u_int32_t flags; + int32_t fileid; + DB_LSN * lsn; + db_pgno_t pgno; + u_int32_t indx; + db_recno_t recno; + const DBT *data; +{ + DBT logrec; + DB_LSN *lsnp, null_lsn; + u_int32_t zero; + u_int32_t rectype, txn_num; + int ret; + u_int8_t *bp; + + rectype = DB_qam_delext; + if (txnid != NULL && + TAILQ_FIRST(&txnid->kids) != NULL && + (ret = __txn_activekids(dbenv, rectype, txnid)) != 0) + return (ret); + txn_num = txnid == NULL ? 0 : txnid->txnid; + if (txnid == NULL) { + ZERO_LSN(null_lsn); + lsnp = &null_lsn; + } else + lsnp = &txnid->last_lsn; + logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN) + + sizeof(fileid) + + sizeof(*lsn) + + sizeof(pgno) + + sizeof(indx) + + sizeof(recno) + + sizeof(u_int32_t) + (data == NULL ? 0 : data->size); + if ((ret = __os_malloc(dbenv, logrec.size, NULL, &logrec.data)) != 0) + return (ret); + + bp = logrec.data; + memcpy(bp, &rectype, sizeof(rectype)); + bp += sizeof(rectype); + memcpy(bp, &txn_num, sizeof(txn_num)); + bp += sizeof(txn_num); + memcpy(bp, lsnp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(bp, &fileid, sizeof(fileid)); + bp += sizeof(fileid); + if (lsn != NULL) + memcpy(bp, lsn, sizeof(*lsn)); + else + memset(bp, 0, sizeof(*lsn)); + bp += sizeof(*lsn); + memcpy(bp, &pgno, sizeof(pgno)); + bp += sizeof(pgno); + memcpy(bp, &indx, sizeof(indx)); + bp += sizeof(indx); + memcpy(bp, &recno, sizeof(recno)); + bp += sizeof(recno); + if (data == NULL) { + zero = 0; + memcpy(bp, &zero, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + } else { + memcpy(bp, &data->size, sizeof(data->size)); + bp += sizeof(data->size); + memcpy(bp, data->data, data->size); + bp += data->size; + } + DB_ASSERT((u_int32_t)(bp - (u_int8_t *)logrec.data) == logrec.size); + ret = log_put(dbenv, ret_lsnp, (DBT *)&logrec, flags); + if (txnid != NULL) + txnid->last_lsn = *ret_lsnp; + __os_free(logrec.data, logrec.size); + return (ret); +} + +int +__qam_delext_print(dbenv, dbtp, lsnp, notused2, notused3) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops notused2; + void *notused3; +{ + __qam_delext_args *argp; + u_int32_t i; + u_int ch; + int ret; + + i = 0; + ch = 0; + notused2 = DB_TXN_ABORT; + notused3 = NULL; + + if ((ret = __qam_delext_read(dbenv, dbtp->data, &argp)) != 0) + return (ret); + printf("[%lu][%lu]qam_delext: rec: %lu txnid %lx prevlsn [%lu][%lu]\n", + (u_long)lsnp->file, + (u_long)lsnp->offset, + (u_long)argp->type, + (u_long)argp->txnid->txnid, + (u_long)argp->prev_lsn.file, + (u_long)argp->prev_lsn.offset); + printf("\tfileid: %ld\n", (long)argp->fileid); + printf("\tlsn: [%lu][%lu]\n", + (u_long)argp->lsn.file, (u_long)argp->lsn.offset); + printf("\tpgno: %lu\n", (u_long)argp->pgno); + printf("\tindx: %lu\n", (u_long)argp->indx); + printf("\trecno: %lu\n", (u_long)argp->recno); + printf("\tdata: "); + for (i = 0; i < argp->data.size; i++) { + ch = ((u_int8_t *)argp->data.data)[i]; + if (isprint(ch) || ch == 0xa) + putchar(ch); + else + printf("%#x ", ch); + } + printf("\n"); + printf("\n"); + __os_free(argp, 0); + return (0); +} + +int +__qam_delext_read(dbenv, recbuf, argpp) + DB_ENV *dbenv; + void *recbuf; + __qam_delext_args **argpp; +{ + __qam_delext_args *argp; + u_int8_t *bp; + int ret; + + ret = __os_malloc(dbenv, sizeof(__qam_delext_args) + + sizeof(DB_TXN), NULL, &argp); + if (ret != 0) + return (ret); + argp->txnid = (DB_TXN *)&argp[1]; + bp = recbuf; + memcpy(&argp->type, bp, sizeof(argp->type)); + bp += sizeof(argp->type); + memcpy(&argp->txnid->txnid, bp, sizeof(argp->txnid->txnid)); + bp += sizeof(argp->txnid->txnid); + memcpy(&argp->prev_lsn, bp, sizeof(DB_LSN)); + bp += sizeof(DB_LSN); + memcpy(&argp->fileid, bp, sizeof(argp->fileid)); + bp += sizeof(argp->fileid); + memcpy(&argp->lsn, bp, sizeof(argp->lsn)); + bp += sizeof(argp->lsn); + memcpy(&argp->pgno, bp, sizeof(argp->pgno)); + bp += sizeof(argp->pgno); + memcpy(&argp->indx, bp, sizeof(argp->indx)); + bp += sizeof(argp->indx); + memcpy(&argp->recno, bp, sizeof(argp->recno)); + bp += sizeof(argp->recno); + memset(&argp->data, 0, sizeof(argp->data)); + memcpy(&argp->data.size, bp, sizeof(u_int32_t)); + bp += sizeof(u_int32_t); + argp->data.data = bp; + bp += argp->data.size; + *argpp = argp; + return (0); +} + +int +__qam_init_print(dbenv) + DB_ENV *dbenv; +{ + int ret; + + if ((ret = __db_add_recovery(dbenv, + __qam_inc_print, DB_qam_inc)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_incfirst_print, DB_qam_incfirst)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_mvptr_print, DB_qam_mvptr)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_del_print, DB_qam_del)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_add_print, DB_qam_add)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_delete_print, DB_qam_delete)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_rename_print, DB_qam_rename)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_delext_print, DB_qam_delext)) != 0) + return (ret); + return (0); +} + +int +__qam_init_recover(dbenv) + DB_ENV *dbenv; +{ + int ret; + + if ((ret = __db_add_recovery(dbenv, + __qam_inc_recover, DB_qam_inc)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_incfirst_recover, DB_qam_incfirst)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_mvptr_recover, DB_qam_mvptr)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_del_recover, DB_qam_del)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_add_recover, DB_qam_add)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_delete_recover, DB_qam_delete)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_rename_recover, DB_qam_rename)) != 0) + return (ret); + if ((ret = __db_add_recovery(dbenv, + __qam_delext_recover, DB_qam_delext)) != 0) + return (ret); + return (0); +} + diff --git a/db/qam/qam_conv.c b/db/qam/qam_conv.c new file mode 100644 index 000000000..2eb1c7227 --- /dev/null +++ b/db/qam/qam_conv.c @@ -0,0 +1,83 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam_conv.c,v 11.6 2000/11/16 23:40:57 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "qam.h" +#include "db_swap.h" +#include "db_am.h" + +/* + * __qam_mswap -- + * Swap the bytes on the queue metadata page. + * + * PUBLIC: int __qam_mswap __P((PAGE *)); + */ +int +__qam_mswap(pg) + PAGE *pg; +{ + u_int8_t *p; + + __db_metaswap(pg); + + p = (u_int8_t *)pg + sizeof(DBMETA); + + SWAP32(p); /* first_recno */ + SWAP32(p); /* cur_recno */ + SWAP32(p); /* re_len */ + SWAP32(p); /* re_pad */ + SWAP32(p); /* rec_page */ + SWAP32(p); /* page_ext */ + + return (0); +} + +/* + * __qam_pgin_out -- + * Convert host-specific page layout to/from the host-independent format + * stored on disk. + * We only need to fix up a few fields in the header + * + * PUBLIC: int __qam_pgin_out __P((DB_ENV *, db_pgno_t, void *, DBT *)); + */ +int +__qam_pgin_out(dbenv, pg, pp, cookie) + DB_ENV *dbenv; + db_pgno_t pg; + void *pp; + DBT *cookie; +{ + DB_PGINFO *pginfo; + QPAGE *h; + + COMPQUIET(pg, 0); + COMPQUIET(dbenv, NULL); + pginfo = (DB_PGINFO *)cookie->data; + if (!pginfo->needswap) + return (0); + + h = pp; + if (h->type == P_QAMMETA) + return (__qam_mswap(pp)); + + M_32_SWAP(h->lsn.file); + M_32_SWAP(h->lsn.offset); + M_32_SWAP(h->pgno); + + return (0); +} diff --git a/db/qam/qam_files.c b/db/qam/qam_files.c new file mode 100644 index 000000000..e53a3bf24 --- /dev/null +++ b/db/qam/qam_files.c @@ -0,0 +1,503 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam_files.c,v 1.16 2001/01/19 18:01:59 bostic Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_shash.h" +#include "db_am.h" +#include "lock.h" +#include "btree.h" +#include "qam.h" +#include "mp.h" + +/* + * __qam_fprobe -- calcluate and open extent + * + * Calculate which extent the page is in, open and create + * if necessary. + * + * PUBLIC: int __qam_fprobe __P((DB *, db_pgno_t, void *, qam_probe_mode, int)); + */ + +int +__qam_fprobe(dbp, pgno, addrp, mode, flags) + DB *dbp; + db_pgno_t pgno; + void *addrp; + qam_probe_mode mode; + int flags; +{ + DB_ENV *dbenv; + DB_MPOOLFILE *mpf; + MPFARRAY *array; + QUEUE *qp; + u_int32_t extid, maxext; + char buf[256]; + int numext, offset, oldext, openflags, ret; + + qp = (QUEUE *)dbp->q_internal; + if (qp->page_ext == 0) { + mpf = dbp->mpf; + if (mode == QAM_PROBE_GET) + return (memp_fget(mpf, &pgno, flags, addrp)); + return (memp_fput(mpf, addrp, flags)); + } + + dbenv = dbp->dbenv; + mpf = NULL; + ret = 0; + + /* + * Need to lock long enough to find the mpf or create the file. + * The file cannot go away because we must have a record locked + * in that file. + */ + MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); + extid = (pgno - 1) / qp->page_ext; + + /* Array1 will always be in use if array2 is in use. */ + array = &qp->array1; + if (array->n_extent == 0) { + /* Start with 4 extents */ + oldext = 0; + array->n_extent = 4; + array->low_extent = extid; + offset = 0; + numext = 0; + goto alloc; + } + + offset = extid - qp->array1.low_extent; + if (qp->array2.n_extent != 0 && + abs(offset) > abs(extid - qp->array2.low_extent)) { + array = &qp->array2; + offset = extid - array->low_extent; + } + + /* + * Check to see if the requested extent is outside the range of + * extents in the array. This is true by defualt if there are + * no extents here yet. + */ + if (offset < 0 || (unsigned) offset >= array->n_extent) { + oldext = array->n_extent; + numext = array->hi_extent - array->low_extent + 1; + if (offset < 0 + && (unsigned) -offset + numext <= array->n_extent) { + /* If we can fit this one in, move the array up */ + memmove(&array->mpfarray[-offset], + array->mpfarray, numext + * sizeof(array->mpfarray[0])); + memset(array->mpfarray, 0, -offset + * sizeof(array->mpfarray[0])); + offset = 0; + } else if ((u_int32_t)offset == array->n_extent && + mode != QAM_PROBE_MPF && array->mpfarray[0].pinref == 0) { + /* We can close the bottom extent. */ + mpf = array->mpfarray[0].mpf; + if (mpf != NULL && (ret = memp_fclose(mpf)) != 0) + goto err; + memmove(&array->mpfarray[0], &array->mpfarray[1], + (array->n_extent - 1) * sizeof (array->mpfarray[0])); + array->low_extent++; + array->hi_extent++; + offset--; + array->mpfarray[offset].mpf = NULL; + array->mpfarray[offset].pinref = 0; + } else { + /* See if we have wrapped around the queue. */ + maxext = (u_int32_t) UINT32_T_MAX + / (qp->page_ext * qp->rec_page); + if ((u_int32_t) abs(offset) >= maxext/2) { + array = &qp->array2; + DB_ASSERT(array->n_extent == 0); + oldext = 0; + array->n_extent = 4; + array->low_extent = extid; + offset = 0; + numext = 0; + } else { + /* + * Increase the size to at least include + * the new one and double it. + */ + array->n_extent += abs(offset); + array->n_extent <<= 2; + } + alloc: + if ((ret = __os_realloc(dbenv, + array->n_extent * sizeof(struct __qmpf), + NULL, &array->mpfarray)) != 0) + goto err; + + if (offset < 0) { + offset = -offset; + memmove(&array->mpfarray[offset], array->mpfarray, + numext * sizeof(array->mpfarray[0])); + memset(array->mpfarray, 0, + offset * sizeof(array->mpfarray[0])); + memset(&array->mpfarray[numext + offset], 0, + (array->n_extent - (numext + offset)) + * sizeof(array->mpfarray[0])); + offset = 0; + } + else + memset(&array->mpfarray[oldext], 0, + (array->n_extent - oldext) * + sizeof(array->mpfarray[0])); + } + } + + if (extid < array->low_extent) + array->low_extent = extid; + if (extid > array->hi_extent) + array->hi_extent = extid; + if (array->mpfarray[offset].mpf == NULL) { + snprintf(buf, + sizeof(buf), QUEUE_EXTENT, qp->dir, qp->name, extid); + openflags = DB_EXTENT; + if (LF_ISSET(DB_MPOOL_CREATE)) + openflags |= DB_CREATE; + if (F_ISSET(dbp, DB_AM_RDONLY)) + openflags |= DB_RDONLY; + qp->finfo.fileid = NULL; + if ((ret = __memp_fopen(dbenv->mp_handle, + NULL, buf, openflags, qp->mode, dbp->pgsize, + 1, &qp->finfo, &array->mpfarray[offset].mpf)) != 0) + goto err; + } + + mpf = array->mpfarray[offset].mpf; + if (mode == QAM_PROBE_GET) + array->mpfarray[offset].pinref++; + if (LF_ISSET(DB_MPOOL_CREATE)) + __memp_clear_unlink(mpf); + +err: + MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); + + if (ret == 0) { + if (mode == QAM_PROBE_MPF) { + *(DB_MPOOLFILE **)addrp = mpf; + return (0); + } + pgno--; + pgno %= qp->page_ext; + if (mode == QAM_PROBE_GET) + return (memp_fget(mpf, + &pgno, flags | DB_MPOOL_EXTENT, addrp)); + ret = memp_fput(mpf, addrp, flags); + MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); + array->mpfarray[offset].pinref--; + MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); + } + return (ret); +} + +/* + * __qam_fclose -- close an extent. + * + * Calculate which extent the page is in and close it. + * We assume the mpf entry is present. + * + * PUBLIC: int __qam_fclose __P((DB *, db_pgno_t)); + */ + +int +__qam_fclose(dbp, pgnoaddr) + DB *dbp; + db_pgno_t pgnoaddr; +{ + DB_ENV *dbenv; + DB_MPOOLFILE *mpf; + MPFARRAY *array; + QUEUE *qp; + u_int32_t extid; + int offset, ret; + + ret = 0; + dbenv = dbp->dbenv; + qp = (QUEUE *)dbp->q_internal; + + MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); + + extid = (pgnoaddr - 1) / qp->page_ext; + array = &qp->array1; + if (array->low_extent > extid || array->hi_extent < extid) + array = &qp->array2; + offset = extid - array->low_extent; + + DB_ASSERT(offset >= 0 && (unsigned) offset < array->n_extent); + + /* If other threads are still using this file, leave it. */ + if (array->mpfarray[offset].pinref != 0) + goto done; + + mpf = array->mpfarray[offset].mpf; + array->mpfarray[offset].mpf = NULL; + ret = memp_fclose(mpf); + +done: + MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); + return (ret); +} +/* + * __qam_fremove -- remove an extent. + * + * Calculate which extent the page is in and remove it. There is no way + * to remove an extent without probing it first and seeing that is is empty + * so we assume the mpf entry is present. + * + * PUBLIC: int __qam_fremove __P((DB *, db_pgno_t)); + */ + +int +__qam_fremove(dbp, pgnoaddr) + DB *dbp; + db_pgno_t pgnoaddr; +{ + DB_ENV *dbenv; + DB_MPOOLFILE *mpf; + MPFARRAY *array; + QUEUE *qp; + u_int32_t extid; +#if CONFIG_TEST + char buf[256], *real_name; +#endif + int offset, ret; + + qp = (QUEUE *)dbp->q_internal; + dbenv = dbp->dbenv; + ret = 0; + + MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); + + extid = (pgnoaddr - 1) / qp->page_ext; + array = &qp->array1; + if (array->low_extent > extid || array->hi_extent < extid) + array = &qp->array2; + offset = extid - array->low_extent; + + DB_ASSERT(offset >= 0 && (unsigned) offset < array->n_extent); + +#if CONFIG_TEST + real_name = NULL; + /* Find the real name of the file. */ + snprintf(buf, sizeof(buf), + QUEUE_EXTENT, qp->dir, qp->name, extid); + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, buf, 0, NULL, &real_name)) != 0) + goto err; +#endif + mpf = array->mpfarray[offset].mpf; + array->mpfarray[offset].mpf = NULL; + __memp_set_unlink(mpf); + if ((ret = memp_fclose(mpf)) != 0) + goto err; + + if (offset == 0) { + memmove(array->mpfarray, &array->mpfarray[1], + (array->hi_extent - array->low_extent) + * sizeof(array->mpfarray[0])); + array->mpfarray[array->hi_extent - array->low_extent].mpf = NULL; + if (array->low_extent != array->hi_extent) + array->low_extent++; + } else { + if (extid == array->hi_extent) + array->hi_extent--; + } + +err: + MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); +#if CONFIG_TEST + if (real_name != NULL) + __os_freestr(real_name); +#endif + return (ret); +} + +/* + * __qam_sync -- + * Flush the database cache. + * + * PUBLIC: int __qam_sync __P((DB *, u_int32_t)); + */ +int +__qam_sync(dbp, flags) + DB *dbp; + u_int32_t flags; +{ + DB_ENV *dbenv; + DB_MPOOLFILE *mpf; + MPFARRAY *array; + QUEUE *qp; + QUEUE_FILELIST *filelist; + struct __qmpf *mpfp; + u_int32_t i; + int done, ret; + + dbenv = dbp->dbenv; + + PANIC_CHECK(dbenv); + DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->sync"); + + if ((ret = __db_syncchk(dbp, flags)) != 0) + return (ret); + + /* Read-only trees never need to be sync'd. */ + if (F_ISSET(dbp, DB_AM_RDONLY)) + return (0); + + /* If the tree was never backed by a database file, we're done. */ + if (F_ISSET(dbp, DB_AM_INMEM)) + return (0); + + /* Flush any dirty pages from the cache to the backing file. */ + if ((ret = memp_fsync(dbp->mpf)) != 0) + return (ret); + + qp = (QUEUE *)dbp->q_internal; + if (qp->page_ext == 0) + return (0); + + /* We do this for the side effect of opening all active extents. */ + if ((ret = __qam_gen_filelist(dbp, &filelist)) != 0) + return (ret); + + if (filelist == NULL) + return (0); + + __os_free(filelist, 0); + + done = 0; + qp = (QUEUE *)dbp->q_internal; + array = &qp->array1; + + MUTEX_THREAD_LOCK(dbenv, dbp->mutexp); +again: + mpfp = array->mpfarray; + for (i = array->low_extent; i <= array->hi_extent; i++, mpfp++) + if ((mpf = mpfp->mpf) != NULL) { + if ((ret = memp_fsync(mpf)) != 0) + goto err; + /* + * If we are the only ones with this file open + * then close it so it might be removed. + */ + if (mpfp->pinref == 0) { + mpfp->mpf = NULL; + if ((ret = memp_fclose(mpf)) != 0) + goto err; + } + } + + if (done == 0 && qp->array2.n_extent != 0) { + array = &qp->array2; + done = 1; + goto again; + } + +err: + MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp); + return (ret); +} + +/* + * __qam_gen_filelist -- generate a list of extent files. + * Another thread may close the handle so this should only + * be used single threaded or with care. + * + * PUBLIC: int __qam_gen_filelist __P(( DB *, QUEUE_FILELIST **)); + */ +int +__qam_gen_filelist(dbp, filelistp) + DB *dbp; + QUEUE_FILELIST **filelistp; +{ + DB_ENV *dbenv; + QUEUE *qp; + QMETA *meta; + db_pgno_t i, last, start, stop; + db_recno_t current, first; + QUEUE_FILELIST *fp; + int ret; + + dbenv = dbp->dbenv; + qp = (QUEUE *)dbp->q_internal; + *filelistp = NULL; + if (qp->page_ext == 0) + return (0); + + /* This may happen during metapage recovery. */ + if (qp->name == NULL) + return (0); + + /* Find out the page number of the last page in the database. */ + i = PGNO_BASE_MD; + if ((ret = memp_fget(dbp->mpf, &i, 0, &meta)) != 0) { + (void)dbp->close(dbp, 0); + return (ret); + } + + current = meta->cur_recno; + first = meta->first_recno; + + if ((ret = memp_fput(dbp->mpf, meta, 0)) != 0) { + (void)dbp->close(dbp, 0); + return (ret); + } + + last = QAM_RECNO_PAGE(dbp, current); + start = QAM_RECNO_PAGE(dbp, first); + + /* Allocate the worst case plus 1 for null termination. */ + if (last >= start) + ret = last - start + 2; + else + ret = last + (QAM_RECNO_PAGE(dbp, UINT32_T_MAX) - start) + 1; + if ((ret = __os_calloc(dbenv, + ret, sizeof(QUEUE_FILELIST), filelistp)) != 0) + return (ret); + fp = *filelistp; + i = start; + if (last >= start) + stop = last; + else + stop = QAM_RECNO_PAGE(dbp, UINT32_T_MAX); +again: + for (; i <= last; i += qp->page_ext) { + if ((ret = __qam_fprobe(dbp, + i, &fp->mpf, QAM_PROBE_MPF, 0)) != 0) { + if (ret == ENOENT) + continue; + return (ret); + } + fp->id = (i - 1) / qp->page_ext; + fp++; + } + + if (last < start) { + i = 1; + stop = last; + start = 0; + goto again; + } + + return (0); +} diff --git a/db/qam/qam_method.c b/db/qam/qam_method.c new file mode 100644 index 000000000..1c94f4b8d --- /dev/null +++ b/db/qam/qam_method.c @@ -0,0 +1,472 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam_method.c,v 11.17 2001/01/10 04:50:54 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_int.h" +#include "db_shash.h" +#include "db_am.h" +#include "qam.h" +#include "db.h" +#include "mp.h" +#include "lock.h" +#include "log.h" + +static int __qam_set_extentsize __P((DB *, u_int32_t)); +static int __qam_remove_callback __P((DB *, void *)); + +struct __qam_cookie { + DB_LSN lsn; + QUEUE_FILELIST *filelist; +}; + +/* + * __qam_db_create -- + * Queue specific initialization of the DB structure. + * + * PUBLIC: int __qam_db_create __P((DB *)); + */ +int +__qam_db_create(dbp) + DB *dbp; +{ + QUEUE *t; + int ret; + + /* Allocate and initialize the private queue structure. */ + if ((ret = __os_calloc(dbp->dbenv, 1, sizeof(QUEUE), &t)) != 0) + return (ret); + dbp->q_internal = t; + dbp->set_q_extentsize = __qam_set_extentsize; + + t->re_pad = ' '; + + return (0); +} + +/* + * __qam_db_close -- + * Queue specific discard of the DB structure. + * + * PUBLIC: int __qam_db_close __P((DB *)); + */ +int +__qam_db_close(dbp) + DB *dbp; +{ + DB_MPOOLFILE *mpf; + MPFARRAY *array; + QUEUE *t; + struct __qmpf *mpfp; + u_int32_t i; + int ret, t_ret; + + ret = 0; + t = dbp->q_internal; + + array = &t->array1; +again: + mpfp = array->mpfarray; + if (mpfp != NULL) { + for (i = array->low_extent; + i <= array->hi_extent; i++, mpfp++) { + mpf = mpfp->mpf; + mpfp->mpf = NULL; + if (mpf != NULL && + (t_ret = memp_fclose(mpf)) != 0 && ret == 0) + ret = t_ret; + } + __os_free(array->mpfarray, 0); + } + if (t->array2.n_extent != 0) { + array = &t->array2; + array->n_extent = 0; + goto again; + } + + if (t->path != NULL) + __os_free(t->path, 0); + __os_free(t, sizeof(QUEUE)); + dbp->q_internal = NULL; + + return (ret); +} + +static int +__qam_set_extentsize(dbp, extentsize) + DB *dbp; + u_int32_t extentsize; +{ + DB_ILLEGAL_AFTER_OPEN(dbp, "set_extentsize"); + + if (extentsize < 1) { + __db_err(dbp->dbenv, "Extent size must be at least 1."); + return (EINVAL); + } + + ((QUEUE*)dbp->q_internal)->page_ext = extentsize; + + return (0); +} + +/* + * __db_prqueue -- + * Print out a queue + * + * PUBLIC: int __db_prqueue __P((DB *, u_int32_t)); + */ +int +__db_prqueue(dbp, flags) + DB *dbp; + u_int32_t flags; +{ + PAGE *h; + QMETA *meta; + db_pgno_t first, i, last, pg_ext, stop; + int ret; + + /* Find out the page number of the last page in the database. */ + i = PGNO_BASE_MD; + if ((ret = memp_fget(dbp->mpf, &i, 0, &meta)) != 0) + return (ret); + + first = QAM_RECNO_PAGE(dbp, meta->first_recno); + last = QAM_RECNO_PAGE(dbp, meta->cur_recno); + + if ((ret = __db_prpage(dbp, (PAGE *)meta, flags)) != 0) + return (ret); + if ((ret = memp_fput(dbp->mpf, meta, 0)) != 0) + return (ret); + + i = first; + if (first > last) + stop = QAM_RECNO_PAGE(dbp, UINT32_T_MAX); + else + stop = last; + + /* Dump each page. */ +begin: + for (; i <= stop; ++i) { + if ((ret = __qam_fget(dbp, &i, DB_MPOOL_EXTENT, &h)) != 0) { + pg_ext = ((QUEUE *)dbp->q_internal)->page_ext; + if (pg_ext == 0) { + if (ret == EINVAL && first == last) + return (0); + return (ret); + } + if (ret == ENOENT || ret == EINVAL) { + i += pg_ext - ((i - 1) % pg_ext) - 1; + continue; + } + return (ret); + } + (void)__db_prpage(dbp, h, flags); + if ((ret = __qam_fput(dbp, i, h, 0)) != 0) + return (ret); + } + + if (first > last) { + i = 1; + stop = last; + first = last; + goto begin; + } + return (0); +} + +/* + * __qam_remove + * Remove method for a Queue. + * + * PUBLIC: int __qam_remove __P((DB *, const char *, + * PUBLIC: const char *, DB_LSN *, int (**)(DB *, void*), void **)); + */ +int +__qam_remove(dbp, name, subdb, lsnp, callbackp, cookiep) + DB *dbp; + const char *name, *subdb; + DB_LSN *lsnp; + int (**callbackp) __P((DB *, void *)); + void **cookiep; +{ + DBT namedbt; + DB_ENV *dbenv; + DB_LSN lsn; + MPFARRAY *ap; + QUEUE *qp; + int ret; + char *backup, buf[256], *real_back, *real_name; + QUEUE_FILELIST *filelist, *fp; + struct __qam_cookie *qam_cookie; + + dbenv = dbp->dbenv; + ret = 0; + backup = real_back = real_name = NULL; + filelist = NULL; + + PANIC_CHECK(dbenv); + + /* + * Subdatabases. + */ + if (subdb != NULL) { + __db_err(dbenv, + "Queue does not support multiple databases per file."); + ret = EINVAL; + goto done; + } + + qp = (QUEUE *)dbp->q_internal; + + if (qp->page_ext != 0 && + (ret = __qam_gen_filelist(dbp, &filelist)) != 0) + goto done; + + if (filelist == NULL) + goto done; + + for (fp = filelist; fp->mpf != NULL; fp++) { + snprintf(buf, + sizeof(buf), QUEUE_EXTENT, qp->dir, qp->name, fp->id); + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, buf, 0, NULL, &real_name)) != 0) + goto done; + if (LOGGING_ON(dbenv)) { + memset(&namedbt, 0, sizeof(namedbt)); + namedbt.data = (char *)buf; + namedbt.size = strlen(buf) + 1; + + if ((ret = + __qam_delete_log(dbenv, dbp->open_txn, + &lsn, DB_FLUSH, &namedbt, lsnp)) != 0) { + __db_err(dbenv, + "%s: %s", name, db_strerror(ret)); + goto done; + } + } + (void)__memp_fremove(fp->mpf); + if ((ret = memp_fclose(fp->mpf)) != 0) + goto done; + if (qp->array2.n_extent == 0 || qp->array2.low_extent > fp->id) + ap = &qp->array1; + else + ap = &qp->array2; + ap->mpfarray[fp->id - ap->low_extent].mpf = NULL; + + /* Create name for backup file. */ + if (TXN_ON(dbenv)) { + if ((ret = __db_backup_name(dbenv, + buf, &backup, lsnp)) != 0) + goto done; + if ((ret = __db_appname(dbenv, DB_APP_DATA, + NULL, backup, 0, NULL, &real_back)) != 0) + goto done; + if ((ret = __os_rename(dbenv, + real_name, real_back)) != 0) + goto done; + __os_freestr(real_back); + real_back = NULL; + } + else + if ((ret = __os_unlink(dbenv, real_name)) != 0) + goto done; + __os_freestr(real_name); + real_name = NULL; + } + if ((ret= __os_malloc(dbenv, + sizeof(struct __qam_cookie), NULL, &qam_cookie)) != 0) + goto done; + qam_cookie->lsn = *lsnp; + qam_cookie->filelist = filelist; + *cookiep = qam_cookie; + *callbackp = __qam_remove_callback; + +done: + if (ret != 0 && filelist != NULL) + __os_free(filelist, 0); + if (real_back != NULL) + __os_freestr(real_back); + if (real_name != NULL) + __os_freestr(real_name); + if (backup != NULL) + __os_freestr(backup); + + return (ret); +} + +static int +__qam_remove_callback(dbp, cookie) + DB *dbp; + void *cookie; +{ + DB_ENV *dbenv; + DB_LSN *lsnp; + QUEUE *qp; + QUEUE_FILELIST *filelist, *fp; + char *backup, buf[256], *real_back; + int ret; + + qp = (QUEUE *)dbp->q_internal; + if (qp->page_ext == 0) + return (__os_unlink(dbp->dbenv, cookie)); + + dbenv = dbp->dbenv; + lsnp = &((struct __qam_cookie *)cookie)->lsn; + filelist = fp = ((struct __qam_cookie *)cookie)->filelist; + real_back = backup = NULL; + if ((ret = + __db_backup_name(dbenv, qp->name, &backup, lsnp)) != 0) + goto err; + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, backup, 0, NULL, &real_back)) != 0) + goto err; + if ((ret = __os_unlink(dbp->dbenv, real_back)) != 0) + goto err; + + __os_freestr(backup); + __os_freestr(real_back); + + if (fp == NULL) + return (0); + + for (; fp->mpf != NULL; fp++) { + snprintf(buf, + sizeof(buf), QUEUE_EXTENT, qp->dir, qp->name, fp->id); + real_back = backup = NULL; + if ((ret = __db_backup_name(dbenv, buf, &backup, lsnp)) != 0) + goto err; + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, backup, 0, NULL, &real_back)) != 0) + goto err; + ret = __os_unlink(dbenv, real_back); + __os_freestr(real_back); + __os_freestr(backup); + } + __os_free(filelist, 0); + __os_free(cookie, sizeof (struct __qam_cookie)); + + return (0); + +err: + if (backup != NULL) + __os_freestr(backup); + + if (real_back != NULL) + __os_freestr(real_back); + + return (ret); +} + +/* + * __qam_rename + * Rename method for Queue. + * + * PUBLIC: int __qam_rename __P((DB *, + * PUBLIC: const char *, const char *, const char *)); + */ +int +__qam_rename(dbp, filename, subdb, newname) + DB *dbp; + const char *filename, *subdb, *newname; +{ + DBT namedbt, newnamedbt; + DB_ENV *dbenv; + DB_LSN newlsn; + MPFARRAY *ap; + QUEUE *qp; + QUEUE_FILELIST *fp, *filelist; + char buf[256], nbuf[256], *namep, *real_name, *real_newname; + int ret; + + dbenv = dbp->dbenv; + ret = 0; + real_name = real_newname = NULL; + filelist = NULL; + + qp = (QUEUE *)dbp->q_internal; + + if (subdb != NULL) { + __db_err(dbenv, + "Queue does not support multiple databases per file."); + ret = EINVAL; + goto err; + } + if (qp->page_ext != 0 && + (ret = __qam_gen_filelist(dbp, &filelist)) != 0) + goto err; + if ((namep = __db_rpath(newname)) != NULL) + newname = namep + 1; + + for (fp = filelist; fp != NULL && fp->mpf != NULL; fp++) { + if ((ret = __memp_fremove(fp->mpf)) != 0) + goto err; + if ((ret = memp_fclose(fp->mpf)) != 0) + goto err; + if (qp->array2.n_extent == 0 || qp->array2.low_extent > fp->id) + ap = &qp->array1; + else + ap = &qp->array2; + ap->mpfarray[fp->id - ap->low_extent].mpf = NULL; + snprintf(buf, + sizeof(buf), QUEUE_EXTENT, qp->dir, qp->name, fp->id); + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, buf, 0, NULL, &real_name)) != 0) + goto err; + snprintf(nbuf, + sizeof(nbuf), QUEUE_EXTENT, qp->dir, newname, fp->id); + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, nbuf, 0, NULL, &real_newname)) != 0) + goto err; + if (LOGGING_ON(dbenv)) { + memset(&namedbt, 0, sizeof(namedbt)); + namedbt.data = (char *)buf; + namedbt.size = strlen(buf) + 1; + + memset(&newnamedbt, 0, sizeof(namedbt)); + newnamedbt.data = (char *)nbuf; + newnamedbt.size = strlen(nbuf) + 1; + + if ((ret = + __qam_rename_log(dbenv, + dbp->open_txn, &newlsn, 0, + &namedbt, &newnamedbt)) != 0) { + __db_err(dbenv, "%s: %s", filename, db_strerror(ret)); + goto err; + } + + if ((ret = __log_filelist_update(dbenv, dbp, + dbp->log_fileid, newname, NULL)) != 0) + goto err; + } + if ((ret = __os_rename(dbenv, real_name, real_newname)) != 0) + goto err; + __os_freestr(real_name); + __os_freestr(real_newname); + real_name = real_newname = NULL; + } + +err: + if (real_name != NULL) + __os_freestr(real_name); + if (real_newname != NULL) + __os_freestr(real_newname); + if (filelist != NULL) + __os_free(filelist, 0); + + return (ret); +} diff --git a/db/qam/qam_open.c b/db/qam/qam_open.c new file mode 100644 index 000000000..73346439f --- /dev/null +++ b/db/qam/qam_open.c @@ -0,0 +1,268 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam_open.c,v 11.31 2000/12/20 17:59:29 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_shash.h" +#include "db_swap.h" +#include "db_am.h" +#include "lock.h" +#include "qam.h" + +/* + * __qam_open + * + * PUBLIC: int __qam_open __P((DB *, const char *, db_pgno_t, int, u_int32_t)); + */ +int +__qam_open(dbp, name, base_pgno, mode, flags) + DB *dbp; + const char *name; + db_pgno_t base_pgno; + int mode; + u_int32_t flags; +{ + QUEUE *t; + DBC *dbc; + DB_LOCK metalock; + DB_LSN orig_lsn; + QMETA *qmeta; + int locked; + int ret, t_ret; + + ret = 0; + locked = 0; + t = dbp->q_internal; + + if (name == NULL && t->page_ext != 0) { + __db_err(dbp->dbenv, + "Extent size may not be specified for in-memory queue database."); + return (EINVAL); + } + /* Initialize the remaining fields/methods of the DB. */ + dbp->del = __qam_delete; + dbp->put = __qam_put; + dbp->stat = __qam_stat; + dbp->sync = __qam_sync; + dbp->db_am_remove = __qam_remove; + dbp->db_am_rename = __qam_rename; + + metalock.off = LOCK_INVALID; + + /* + * Get a cursor. If DB_CREATE is specified, we may be creating + * pages, and to do that safely in CDB we need a write cursor. + * In STD_LOCKING mode, we'll synchronize using the meta page + * lock instead. + */ + if ((ret = dbp->cursor(dbp, dbp->open_txn, + &dbc, LF_ISSET(DB_CREATE) && CDB_LOCKING(dbp->dbenv) ? + DB_WRITECURSOR : 0)) != 0) + return (ret); + + /* Get, and optionally create the metadata page. */ + if ((ret = + __db_lget(dbc, 0, base_pgno, DB_LOCK_READ, 0, &metalock)) != 0) + goto err; + if ((ret = memp_fget( + dbp->mpf, &base_pgno, DB_MPOOL_CREATE, (PAGE **)&qmeta)) != 0) + goto err; + + /* + * If the magic number is correct, we're not creating the tree. + * Correct any fields that may not be right. Note, all of the + * local flags were set by DB->open. + */ +again: if (qmeta->dbmeta.magic == DB_QAMMAGIC) { + t->re_pad = qmeta->re_pad; + t->re_len = qmeta->re_len; + t->rec_page = qmeta->rec_page; + t->page_ext = qmeta->page_ext; + + (void)memp_fput(dbp->mpf, (PAGE *)qmeta, 0); + goto done; + } + + /* If we're doing CDB; we now have to get the write lock. */ + if (CDB_LOCKING(dbp->dbenv)) { + DB_ASSERT(LF_ISSET(DB_CREATE)); + if ((ret = lock_get(dbp->dbenv, dbc->locker, DB_LOCK_UPGRADE, + &dbc->lock_dbt, DB_LOCK_WRITE, &dbc->mylock)) != 0) + goto err; + } + + /* + * If we are doing locking, relase the read lock + * and get a write lock. We want to avoid deadlock. + */ + if (locked == 0 && STD_LOCKING(dbc)) { + if ((ret = __LPUT(dbc, metalock)) != 0) + goto err; + if ((ret = __db_lget(dbc, + 0, base_pgno, DB_LOCK_WRITE, 0, &metalock)) != 0) + goto err; + locked = 1; + goto again; + } + /* Initialize the tree structure metadata information. */ + orig_lsn = qmeta->dbmeta.lsn; + memset(qmeta, 0, sizeof(QMETA)); + ZERO_LSN(qmeta->dbmeta.lsn); + qmeta->dbmeta.pgno = base_pgno; + qmeta->dbmeta.magic = DB_QAMMAGIC; + qmeta->dbmeta.version = DB_QAMVERSION; + qmeta->dbmeta.pagesize = dbp->pgsize; + qmeta->dbmeta.type = P_QAMMETA; + qmeta->re_pad = t->re_pad; + qmeta->re_len = t->re_len; + qmeta->rec_page = CALC_QAM_RECNO_PER_PAGE(dbp); + qmeta->cur_recno = 1; + qmeta->first_recno = 1; + qmeta->page_ext = t->page_ext; + t->rec_page = qmeta->rec_page; + memcpy(qmeta->dbmeta.uid, dbp->fileid, DB_FILE_ID_LEN); + + /* Verify that we can fit at least one record per page. */ + if (QAM_RECNO_PER_PAGE(dbp) < 1) { + __db_err(dbp->dbenv, + "Record size of %lu too large for page size of %lu", + (u_long)t->re_len, (u_long)dbp->pgsize); + (void)memp_fput(dbp->mpf, (PAGE *)qmeta, 0); + ret = EINVAL; + goto err; + } + + if ((ret = __db_log_page(dbp, + name, &orig_lsn, base_pgno, (PAGE *)qmeta)) != 0) + goto err; + + /* Release the metadata page. */ + if ((ret = memp_fput(dbp->mpf, (PAGE *)qmeta, DB_MPOOL_DIRTY)) != 0) + goto err; + DB_TEST_RECOVERY(dbp, DB_TEST_POSTLOG, ret, name); + + /* + * Flush the metadata page to disk. + * + * !!! + * It's not useful to return not-yet-flushed here -- convert it to + * an error. + */ + if ((ret = memp_fsync(dbp->mpf)) == DB_INCOMPLETE) { + __db_err(dbp->dbenv, "Flush of metapage failed"); + ret = EINVAL; + } + DB_TEST_RECOVERY(dbp, DB_TEST_POSTSYNC, ret, name); + +done: t->q_meta = base_pgno; + t->q_root = base_pgno + 1; + + /* Setup information needed to open extents. */ + if (t->page_ext != 0) { + t->finfo.pgcookie = &t->pgcookie; + t->finfo.fileid = NULL; + t->finfo.lsn_offset = 0; + + t->pginfo.db_pagesize = dbp->pgsize; + t->pginfo.needswap = F_ISSET(dbp, DB_AM_SWAP); + t->pgcookie.data = &t->pginfo; + t->pgcookie.size = sizeof(DB_PGINFO); + + if ((ret = __os_strdup(dbp->dbenv, name, &t->path)) != 0) + goto err; + t->dir = t->path; + if ((t->name = __db_rpath(t->path)) == NULL) { + t->name = t->path; + t->dir = PATH_DOT; + } else + *t->name++ = '\0'; + + if (mode == 0) + mode = __db_omode("rwrw--"); + t->mode = mode; + } + +err: +DB_TEST_RECOVERY_LABEL + /* Don't hold the meta page long term. */ + (void)__LPUT(dbc, metalock); + + if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0) + ret = t_ret; + + return (ret); +} + +/* + * __qam_metachk -- + * + * PUBLIC: int __qam_metachk __P((DB *, const char *, QMETA *)); + */ +int +__qam_metachk(dbp, name, qmeta) + DB *dbp; + const char *name; + QMETA *qmeta; +{ + DB_ENV *dbenv; + u_int32_t vers; + int ret; + + dbenv = dbp->dbenv; + + /* + * At this point, all we know is that the magic number is for a Queue. + * Check the version, the database may be out of date. + */ + vers = qmeta->dbmeta.version; + if (F_ISSET(dbp, DB_AM_SWAP)) + M_32_SWAP(vers); + switch (vers) { + case 1: + case 2: + __db_err(dbenv, + "%s: queue version %lu requires a version upgrade", + name, (u_long)vers); + return (DB_OLD_VERSION); + case 3: + break; + default: + __db_err(dbenv, + "%s: unsupported qam version: %lu", name, (u_long)vers); + return (EINVAL); + } + + /* Swap the page if we need to. */ + if (F_ISSET(dbp, DB_AM_SWAP) && (ret = __qam_mswap((PAGE *)qmeta)) != 0) + return (ret); + + /* Check the type. */ + if (dbp->type != DB_QUEUE && dbp->type != DB_UNKNOWN) + return (EINVAL); + dbp->type = DB_QUEUE; + DB_ILLEGAL_METHOD(dbp, DB_OK_QUEUE); + + /* Set the page size. */ + dbp->pgsize = qmeta->dbmeta.pagesize; + + /* Copy the file's ID. */ + memcpy(dbp->fileid, qmeta->dbmeta.uid, DB_FILE_ID_LEN); + + return (0); +} diff --git a/db/qam/qam_rec.c b/db/qam/qam_rec.c new file mode 100644 index 000000000..4d330f586 --- /dev/null +++ b/db/qam/qam_rec.c @@ -0,0 +1,732 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam_rec.c,v 11.34 2001/01/19 18:01:59 bostic Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_shash.h" +#include "lock.h" +#include "db_am.h" +#include "qam.h" +#include "log.h" + +/* + * __qam_inc_recover -- + * Recovery function for inc. + * + * PUBLIC: int __qam_inc_recover __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + */ +int +__qam_inc_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + __qam_inc_args *argp; + DB *file_dbp; + DBC *dbc; + DB_LOCK lock; + DB_MPOOLFILE *mpf; + QMETA *meta; + db_pgno_t metapg; + int cmp_p, modified, ret; + + COMPQUIET(info, NULL); + REC_PRINT(__qam_inc_print); + REC_INTRO(__qam_inc_read, 1); + + metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; + + if ((ret = __db_lget(dbc, + LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) + goto done; + if ((ret = memp_fget(mpf, &metapg, 0, &meta)) != 0) { + if (DB_REDO(op)) { + if ((ret = memp_fget(mpf, + &metapg, DB_MPOOL_CREATE, &meta)) != 0) { + (void)__LPUT(dbc, lock); + goto out; + } + meta->dbmeta.pgno = metapg; + meta->dbmeta.type = P_QAMMETA; + + } else { + *lsnp = argp->prev_lsn; + ret = 0; + (void)__LPUT(dbc, lock); + goto out; + } + } + + modified = 0; + cmp_p = log_compare(&LSN(meta), &argp->lsn); + CHECK_LSN(op, cmp_p, &LSN(meta), &argp->lsn); + + /* + * The cur_recno never goes backwards. It is a point of + * contention among appenders. If one fails cur_recno will + * most likely be beyond that one when it aborts. + * We move it ahead on either an abort or a commit + * and make the LSN reflect that fact. + */ + if (cmp_p == 0) { + modified = 1; + meta->cur_recno++; + if (meta->cur_recno == RECNO_OOB) + meta->cur_recno++; + meta->dbmeta.lsn = *lsnp; + } + if ((ret = memp_fput(mpf, meta, modified ? DB_MPOOL_DIRTY : 0))) + goto out; + + (void)__LPUT(dbc, lock); + +done: *lsnp = argp->prev_lsn; + ret = 0; + +out: REC_CLOSE; +} + +/* + * __qam_incfirst_recover -- + * Recovery function for incfirst. + * + * PUBLIC: int __qam_incfirst_recover + * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + */ +int +__qam_incfirst_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + __qam_incfirst_args *argp; + DB *file_dbp; + DBC *dbc; + DB_LOCK lock; + DB_MPOOLFILE *mpf; + QMETA *meta; + QUEUE_CURSOR *cp; + db_pgno_t metapg; + int exact, modified, ret, rec_ext; + + COMPQUIET(info, NULL); + REC_PRINT(__qam_incfirst_print); + REC_INTRO(__qam_incfirst_read, 1); + + metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; + + if ((ret = __db_lget(dbc, + LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) + goto done; + if ((ret = memp_fget(mpf, &metapg, 0, &meta)) != 0) { + if (DB_REDO(op)) { + if ((ret = memp_fget(mpf, + &metapg, DB_MPOOL_CREATE, &meta)) != 0) { + (void)__LPUT(dbc, lock); + goto out; + } + meta->dbmeta.pgno = metapg; + meta->dbmeta.type = P_QAMMETA; + } else { + *lsnp = argp->prev_lsn; + ret = 0; + (void)__LPUT(dbc, lock); + goto out; + } + } + + modified = 0; + + /* + * Only move first_recno backwards so we pick up the aborted delete. + * When going forward we need to be careful since + * we may have bumped over a locked record. + */ + if (DB_UNDO(op)) { + if (QAM_BEFORE_FIRST(meta, argp->recno)) { + meta->first_recno = argp->recno; + modified = 1; + } + } else { + if (log_compare(&LSN(meta), lsnp) < 0) { + LSN(meta) = *lsnp; + modified = 1; + } + rec_ext = 0; + if (meta->page_ext != 0) + rec_ext = meta->page_ext * meta->rec_page; + cp = (QUEUE_CURSOR *)dbc->internal; + if (meta->first_recno == RECNO_OOB) + meta->first_recno++; + while (meta->first_recno != meta->cur_recno + && !QAM_BEFORE_FIRST(meta, argp->recno + 1)) { + if ((ret = __qam_position(dbc, + &meta->first_recno, QAM_READ, &exact)) != 0) + goto out; + if (cp->page != NULL) + __qam_fput(file_dbp, cp->pgno, cp->page, 0); + + if (exact == 1) + break; + if (cp->page != NULL && + rec_ext != 0 && meta->first_recno % rec_ext == 0) + if ((ret = + __qam_fremove(file_dbp, cp->pgno)) != 0) + goto out; + meta->first_recno++; + if (meta->first_recno == RECNO_OOB) + meta->first_recno++; + modified = 1; + } + } + + if ((ret = memp_fput(mpf, meta, modified ? DB_MPOOL_DIRTY : 0))) + goto out; + + (void)__LPUT(dbc, lock); + +done: *lsnp = argp->prev_lsn; + ret = 0; + +out: REC_CLOSE; +} + +/* + * __qam_mvptr_recover -- + * Recovery function for mvptr. + * + * PUBLIC: int __qam_mvptr_recover + * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + */ +int +__qam_mvptr_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + __qam_mvptr_args *argp; + DB *file_dbp; + DBC *dbc; + DB_LOCK lock; + DB_MPOOLFILE *mpf; + QMETA *meta; + db_pgno_t metapg; + int cmp_p, modified, ret; + + COMPQUIET(info, NULL); + REC_PRINT(__qam_mvptr_print); + REC_INTRO(__qam_mvptr_read, 1); + + metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; + + if ((ret = __db_lget(dbc, + LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) + goto done; + if ((ret = memp_fget(mpf, &metapg, 0, &meta)) != 0) { + if (DB_REDO(op)) { + if ((ret = memp_fget(mpf, + &metapg, DB_MPOOL_CREATE, &meta)) != 0) { + (void)__LPUT(dbc, lock); + goto out; + } + meta->dbmeta.pgno = metapg; + meta->dbmeta.type = P_QAMMETA; + } else { + *lsnp = argp->prev_lsn; + ret = 0; + (void)__LPUT(dbc, lock); + goto out; + } + } + + modified = 0; + cmp_p = log_compare(&meta->dbmeta.lsn, &argp->metalsn); + + /* + * We never undo a movement of one of the pointers. + * Just move them along regardless of abort/commit. + */ + if (cmp_p == 0) { + if (argp->opcode & QAM_SETFIRST) + meta->first_recno = argp->new_first; + + if (argp->opcode & QAM_SETCUR) + meta->cur_recno = argp->new_cur; + + modified = 1; + meta->dbmeta.lsn = *lsnp; + } + + if ((ret = memp_fput(mpf, meta, modified ? DB_MPOOL_DIRTY : 0))) + goto out; + + (void)__LPUT(dbc, lock); + +done: *lsnp = argp->prev_lsn; + ret = 0; + +out: REC_CLOSE; +} +/* + * __qam_del_recover -- + * Recovery function for del. + * Non-extent version or if there is no data (zero len). + * + * PUBLIC: int __qam_del_recover + * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + */ +int +__qam_del_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + __qam_del_args *argp; + DB *file_dbp; + DBC *dbc; + DB_LOCK lock; + DB_MPOOLFILE *mpf; + QAMDATA *qp; + QMETA *meta; + QPAGE *pagep; + db_pgno_t metapg; + int cmp_n, modified, ret; + + COMPQUIET(info, NULL); + REC_PRINT(__qam_del_print); + REC_INTRO(__qam_del_read, 1); + + if ((ret = __qam_fget(file_dbp, + &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0) + goto out; + + modified = 0; + if (pagep->pgno == PGNO_INVALID) { + pagep->pgno = argp->pgno; + pagep->type = P_QAMDATA; + modified = 1; + } + + cmp_n = log_compare(lsnp, &LSN(pagep)); + + if (DB_UNDO(op)) { + /* make sure first is behind us */ + metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; + if ((ret = __db_lget(dbc, + LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) + return (ret); + if ((ret = memp_fget(file_dbp->mpf, &metapg, 0, &meta)) != 0) { + (void)__LPUT(dbc, lock); + goto done; + } + if (meta->first_recno == RECNO_OOB || + (QAM_BEFORE_FIRST(meta, argp->recno) + && (meta->first_recno <= meta->cur_recno + || meta->first_recno - + argp->recno < argp->recno - meta->cur_recno))) { + meta->first_recno = argp->recno; + (void)memp_fput(file_dbp->mpf, meta, DB_MPOOL_DIRTY); + } else + (void)memp_fput(file_dbp->mpf, meta, 0); + (void)__LPUT(dbc, lock); + + /* Need to undo delete - mark the record as present */ + qp = QAM_GET_RECORD(file_dbp, pagep, argp->indx); + F_SET(qp, QAM_VALID); + + /* + * Move the LSN back to this point; do not move it forward. + * Only move it back if we're in recovery. If we're in + * an abort, because we don't hold a page lock, we could + * foul up a concurrent put. Having too late an LSN + * is harmless in queue except when we're determining + * what we need to roll forward during recovery. [#2588] + */ + if (op == DB_TXN_BACKWARD_ROLL && cmp_n < 0) + LSN(pagep) = argp->lsn; + modified = 1; + } else if (cmp_n > 0 && DB_REDO(op)) { + /* Need to redo delete - clear the valid bit */ + qp = QAM_GET_RECORD(file_dbp, pagep, argp->indx); + F_CLR(qp, QAM_VALID); + LSN(pagep) = *lsnp; + modified = 1; + } + if ((ret = __qam_fput(file_dbp, + argp->pgno, pagep, modified ? DB_MPOOL_DIRTY : 0))) + goto out; + +done: *lsnp = argp->prev_lsn; + ret = 0; + +out: REC_CLOSE; +} +/* + * __qam_delext_recover -- + * Recovery function for del in an extent based queue. + * + * PUBLIC: int __qam_delext_recover __P((DB_ENV *, + * PUBLIC: DBT *, DB_LSN *, db_recops, void *)); + */ +int +__qam_delext_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + __qam_delext_args *argp; + DB *file_dbp; + DBC *dbc; + DB_LOCK lock; + DB_MPOOLFILE *mpf; + QAMDATA *qp; + QMETA *meta; + QPAGE *pagep; + db_pgno_t metapg; + int cmp_n, modified, ret; + + COMPQUIET(info, NULL); + REC_PRINT(__qam_delext_print); + REC_INTRO(__qam_delext_read, 1); + + if ((ret = __qam_fget(file_dbp, + &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0) + goto out; + + modified = 0; + if (pagep->pgno == PGNO_INVALID) { + pagep->pgno = argp->pgno; + pagep->type = P_QAMDATA; + modified = 1; + } + + cmp_n = log_compare(lsnp, &LSN(pagep)); + + if (DB_UNDO(op)) { + /* make sure first is behind us */ + metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; + if ((ret = __db_lget(dbc, + LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) + return (ret); + if ((ret = memp_fget(file_dbp->mpf, &metapg, 0, &meta)) != 0) { + (void)__LPUT(dbc, lock); + goto done; + } + if (meta->first_recno == RECNO_OOB || + (QAM_BEFORE_FIRST(meta, argp->recno) + && (meta->first_recno <= meta->cur_recno + || meta->first_recno - + argp->recno < argp->recno - meta->cur_recno))) { + meta->first_recno = argp->recno; + (void)memp_fput(file_dbp->mpf, meta, DB_MPOOL_DIRTY); + } else + (void)memp_fput(file_dbp->mpf, meta, 0); + (void)__LPUT(dbc, lock); + + if ((ret = __qam_pitem(dbc, pagep, + argp->indx, argp->recno, &argp->data)) != 0) + goto done; + + /* + * Move the LSN back to this point; do not move it forward. + * Only move it back if we're in recovery. If we're in + * an abort, because we don't hold a page lock, we could + * foul up a concurrent put. Having too late an LSN + * is harmless in queue except when we're determining + * what we need to roll forward during recovery. [#2588] + */ + if (op == DB_TXN_BACKWARD_ROLL && cmp_n < 0) + LSN(pagep) = argp->lsn; + modified = 1; + } else if (cmp_n > 0 && DB_REDO(op)) { + /* Need to redo delete - clear the valid bit */ + qp = QAM_GET_RECORD(file_dbp, pagep, argp->indx); + F_CLR(qp, QAM_VALID); + LSN(pagep) = *lsnp; + modified = 1; + } + if ((ret = __qam_fput(file_dbp, + argp->pgno, pagep, modified ? DB_MPOOL_DIRTY : 0))) + goto out; + +done: *lsnp = argp->prev_lsn; + ret = 0; + +out: REC_CLOSE; +} + +/* + * __qam_add_recover -- + * Recovery function for add. + * + * PUBLIC: int __qam_add_recover __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + */ +int +__qam_add_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + __qam_add_args *argp; + DB *file_dbp; + DBC *dbc; + DB_MPOOLFILE *mpf; + QAMDATA *qp; + QMETA *meta; + QPAGE *pagep; + db_pgno_t metapg; + int cmp_n, modified, ret; + + COMPQUIET(info, NULL); + REC_PRINT(__qam_add_print); + REC_INTRO(__qam_add_read, 1); + + modified = 0; + if ((ret = __qam_fget(file_dbp, + &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0) + goto out; + + if (pagep->pgno == PGNO_INVALID) { + pagep->pgno = argp->pgno; + pagep->type = P_QAMDATA; + modified = 1; + } + + cmp_n = log_compare(lsnp, &LSN(pagep)); + + if (cmp_n > 0 && DB_REDO(op)) { + /* Need to redo add - put the record on page */ + if ((ret = __qam_pitem(dbc, pagep, argp->indx, argp->recno, + &argp->data)) != 0) + goto err; + LSN(pagep) = *lsnp; + modified = 1; + /* Make sure first pointer includes this record. */ + metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; + if ((ret = memp_fget(mpf, &metapg, 0, &meta)) != 0) + goto err; + if (QAM_BEFORE_FIRST(meta, argp->recno)) { + meta->first_recno = argp->recno; + if ((ret = memp_fput(mpf, meta, DB_MPOOL_DIRTY)) != 0) + goto err; + } else + if ((ret = memp_fput(mpf, meta, 0)) != 0) + goto err; + + } else if (DB_UNDO(op)) { + /* + * Need to undo add + * If this was an overwrite, put old record back. + * Otherwise just clear the valid bit + */ + if (argp->olddata.size != 0) { + if ((ret = __qam_pitem(dbc, pagep, + argp->indx, argp->recno, &argp->olddata)) != 0) + goto err; + + if (!(argp->vflag & QAM_VALID)) { + qp = QAM_GET_RECORD( + file_dbp, pagep, argp->indx); + F_CLR(qp, QAM_VALID); + } + modified = 1; + } else { + qp = QAM_GET_RECORD(file_dbp, pagep, argp->indx); + qp->flags = 0; + modified = 1; + } + + /* + * Move the LSN back to this point; do not move it forward. + * Only move it back if we're in recovery. If we're in + * an abort, because we don't hold a page lock, we could + * foul up a concurrent put. Having too late an LSN + * is harmless in queue except when we're determining + * what we need to roll forward during recovery. [#2588] + */ + if (op == DB_TXN_BACKWARD_ROLL && cmp_n < 0) + LSN(pagep) = argp->lsn; + } + +err: if ((ret = __qam_fput(file_dbp, + argp->pgno, pagep, modified ? DB_MPOOL_DIRTY : 0))) + goto out; + +done: *lsnp = argp->prev_lsn; + ret = 0; + +out: REC_CLOSE; +} +/* + * __qam_delete_recover -- + * Recovery function for delete of an extent. + * + * PUBLIC: int __qam_delete_recover + * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + */ +int +__qam_delete_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + __qam_delete_args *argp; + int ret; + char *backup, *real_back, *real_name; + + COMPQUIET(info, NULL); + + REC_PRINT(__qam_delete_print); + + backup = real_back = real_name = NULL; + if ((ret = __qam_delete_read(dbenv, dbtp->data, &argp)) != 0) + goto out; + + if (DB_REDO(op)) { + /* + * On a recovery, as we recreate what was going on, we + * recreate the creation of the file. And so, even though + * it committed, we need to delete it. Try to delete it, + * but it is not an error if that delete fails. + */ + if ((ret = __db_appname(dbenv, DB_APP_DATA, + NULL, argp->name.data, 0, NULL, &real_name)) != 0) + goto out; + if (__os_exists(real_name, NULL) == 0) { + if ((ret = __os_unlink(dbenv, real_name)) != 0) + goto out; + } + } else if (DB_UNDO(op)) { + /* + * Trying to undo. File may or may not have been deleted. + * Try to move the backup to the original. If the backup + * exists, then this is right. If it doesn't exist, then + * nothing will happen and that's OK. + */ + if ((ret = __db_backup_name(dbenv, argp->name.data, + &backup, &argp->lsn)) != 0) + goto out; + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, backup, 0, NULL, &real_back)) != 0) + goto out; + if ((ret = __db_appname(dbenv, DB_APP_DATA, + NULL, argp->name.data, 0, NULL, &real_name)) != 0) + goto out; + if (__os_exists(real_back, NULL) == 0) + if ((ret = + __os_rename(dbenv, real_back, real_name)) != 0) + goto out; + } + *lsnp = argp->prev_lsn; + ret = 0; + +out: if (argp != NULL) + __os_free(argp, 0); + if (backup != NULL) + __os_freestr(backup); + if (real_back != NULL) + __os_freestr(real_back); + if (real_name != NULL) + __os_freestr(real_name); + return (ret); +} +/* + * __qam_rename_recover -- + * Recovery function for rename. + * + * PUBLIC: int __qam_rename_recover + * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); + */ +int +__qam_rename_recover(dbenv, dbtp, lsnp, op, info) + DB_ENV *dbenv; + DBT *dbtp; + DB_LSN *lsnp; + db_recops op; + void *info; +{ + __qam_rename_args *argp; + char *new_name, *real_name; + int ret; + + COMPQUIET(info, NULL); + + REC_PRINT(__qam_rename_print); + + new_name = real_name = NULL; + + if ((ret = __qam_rename_read(dbenv, dbtp->data, &argp)) != 0) + goto out; + + if (DB_REDO(op)) { + if ((ret = __db_appname(dbenv, DB_APP_DATA, + NULL, argp->name.data, 0, NULL, &real_name)) != 0) + goto out; + if (__os_exists(real_name, NULL) == 0) { + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, argp->newname.data, + 0, NULL, &new_name)) != 0) + goto out; + if ((ret = __os_rename(dbenv, + real_name, new_name)) != 0) + goto out; + } + } else { + if ((ret = __db_appname(dbenv, DB_APP_DATA, + NULL, argp->newname.data, 0, NULL, &new_name)) != 0) + goto out; + if (__os_exists(new_name, NULL) == 0) { + if ((ret = __db_appname(dbenv, + DB_APP_DATA, NULL, argp->name.data, + 0, NULL, &real_name)) != 0) + goto out; + if ((ret = __os_rename(dbenv, + new_name, real_name)) != 0) + goto out; + } + } + + *lsnp = argp->prev_lsn; + ret = 0; + +out: if (argp != NULL) + __os_free(argp, 0); + + if (new_name != NULL) + __os_free(new_name, 0); + + if (real_name != NULL) + __os_free(real_name, 0); + + return (ret); +} diff --git a/db/qam/qam_stat.c b/db/qam/qam_stat.c new file mode 100644 index 000000000..865f477c1 --- /dev/null +++ b/db/qam/qam_stat.c @@ -0,0 +1,201 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam_stat.c,v 11.16 2001/01/10 04:50:54 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_shash.h" +#include "db_am.h" +#include "lock.h" +#include "qam.h" + +/* + * __qam_stat -- + * Gather/print the qam statistics + * + * PUBLIC: int __qam_stat __P((DB *, void *, void *(*)(size_t), u_int32_t)); + */ +int +__qam_stat(dbp, spp, db_malloc, flags) + DB *dbp; + void *spp; + void *(*db_malloc) __P((size_t)); + u_int32_t flags; +{ + QUEUE *t; + DBC *dbc; + DB_LOCK lock; + DB_QUEUE_STAT *sp; + PAGE *h; + QAMDATA *qp, *ep; + QMETA *meta; + db_indx_t indx; + db_pgno_t first, last, pgno, pg_ext, stop; + u_int32_t re_len; + int ret, t_ret; + + PANIC_CHECK(dbp->dbenv); + DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->stat"); + + t = dbp->q_internal; + sp = NULL; + lock.off = LOCK_INVALID; + + /* Check for invalid flags. */ + if ((ret = __db_statchk(dbp, flags)) != 0) + return (ret); + + if (spp == NULL) + return (0); + + /* Acquire a cursor. */ + if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) + return (ret); + + DEBUG_LWRITE(dbc, NULL, "qam_stat", NULL, NULL, flags); + + /* Allocate and clear the structure. */ + if ((ret = __os_malloc(dbp->dbenv, sizeof(*sp), db_malloc, &sp)) != 0) + goto err; + memset(sp, 0, sizeof(*sp)); + + re_len = ((QUEUE *)dbp->q_internal)->re_len; + if (flags == DB_CACHED_COUNTS) { + if ((ret = __db_lget(dbc, + 0, t->q_meta, DB_LOCK_READ, 0, &lock)) != 0) + goto err; + if ((ret = + memp_fget(dbp->mpf, &t->q_meta, 0, (PAGE **)&meta)) != 0) + goto err; + sp->qs_nkeys = meta->dbmeta.key_count; + sp->qs_ndata = meta->dbmeta.record_count; + + goto done; + } + + /* Determine the last page of the database. */ + if ((ret = __db_lget(dbc, + 0, t->q_meta, DB_LOCK_READ, 0, &lock)) != 0) + goto err; + if ((ret = memp_fget(dbp->mpf, &t->q_meta, 0, (PAGE **)&meta)) != 0) + goto err; + + first = QAM_RECNO_PAGE(dbp, meta->first_recno); + last = QAM_RECNO_PAGE(dbp, meta->cur_recno); + + if ((ret = memp_fput(dbp->mpf, meta, 0)) != 0) + goto err; + (void)__LPUT(dbc, lock); + + pgno = first; + if (first > last) + stop = QAM_RECNO_PAGE(dbp, UINT32_T_MAX); + else + stop = last; + + /* Dump each page. */ + pg_ext = ((QUEUE *)dbp->q_internal)->page_ext; +begin: + /* Walk through the pages and count. */ + for (; pgno <= stop; ++pgno) { + if ((ret = + __db_lget(dbc, + 0, pgno, DB_LOCK_READ, 0, &lock)) != 0) + goto err; + ret = __qam_fget(dbp, &pgno, DB_MPOOL_EXTENT, &h); + if (ret == ENOENT) { + pgno += pg_ext - 1; + continue; + } + if (ret == EINVAL) { + pgno += pg_ext - ((pgno - 1) % pg_ext) - 1; + continue; + } + if (ret == EIO && first == last && pg_ext == 0) + break; + if (ret != 0) + goto err; + + ++sp->qs_pages; + + ep = (QAMDATA *)((u_int8_t *)h + dbp->pgsize - re_len); + for (indx = 0, qp = QAM_GET_RECORD(dbp, h, indx); + qp <= ep; + ++indx, qp = QAM_GET_RECORD(dbp, h, indx)) { + if (F_ISSET(qp, QAM_VALID)) + sp->qs_ndata++; + else + sp->qs_pgfree += re_len; + } + + if ((ret = __qam_fput(dbp, pgno, h, 0)) != 0) + goto err; + (void)__LPUT(dbc, lock); + } + if (first > last) { + pgno = 1; + stop = last; + first = last; + goto begin; + } + + /* Get the meta-data page. */ + if ((ret = __db_lget(dbc, + 0, t->q_meta, F_ISSET(dbp, DB_AM_RDONLY) ? + DB_LOCK_READ : DB_LOCK_WRITE, 0, &lock)) != 0) + goto err; + if ((ret = memp_fget(dbp->mpf, &t->q_meta, 0, (PAGE **)&meta)) != 0) + goto err; + + /* Get the metadata fields. */ + sp->qs_magic = meta->dbmeta.magic; + sp->qs_version = meta->dbmeta.version; + sp->qs_metaflags = meta->dbmeta.flags; + sp->qs_pagesize = meta->dbmeta.pagesize; + sp->qs_re_len = meta->re_len; + sp->qs_re_pad = meta->re_pad; + sp->qs_first_recno = meta->first_recno; + sp->qs_cur_recno = meta->cur_recno; + sp->qs_nkeys = sp->qs_ndata; + if (!F_ISSET(dbp, DB_AM_RDONLY)) + meta->dbmeta.key_count = + meta->dbmeta.record_count = sp->qs_ndata; + +done: + /* Discard the meta-data page. */ + if ((ret = memp_fput(dbp->mpf, + meta, F_ISSET(dbp, DB_AM_RDONLY) ? 0 : DB_MPOOL_DIRTY)) != 0) + goto err; + (void)__LPUT(dbc, lock); + + *(DB_QUEUE_STAT **)spp = sp; + ret = 0; + + if (0) { +err: if (sp != NULL) + __os_free(sp, sizeof(*sp)); + } + + if (lock.off != LOCK_INVALID) + (void)__LPUT(dbc, lock); + + if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0) + ret = t_ret; + + return (ret); +} diff --git a/db/qam/qam_upgrade.c b/db/qam/qam_upgrade.c new file mode 100644 index 000000000..f49bfe88d --- /dev/null +++ b/db/qam/qam_upgrade.c @@ -0,0 +1,111 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + */ +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam_upgrade.c,v 11.7 2000/11/30 00:58:44 ubell Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <limits.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_swap.h" +#include "db_am.h" +#include "db_upgrade.h" + +/* + * __qam_31_qammeta -- + * Upgrade the database from version 1 to version 2. + * + * PUBLIC: int __qam_31_qammeta __P((DB *, char *, u_int8_t *)); + */ +int +__qam_31_qammeta(dbp, real_name, buf) + DB *dbp; + char *real_name; + u_int8_t *buf; +{ + QMETA31 *newmeta; + QMETA30 *oldmeta; + + COMPQUIET(dbp, NULL); + COMPQUIET(real_name, NULL); + + newmeta = (QMETA31 *)buf; + oldmeta = (QMETA30 *)buf; + + /* + * Copy the fields to their new locations. + * They may overlap so start at the bottom and use memmove(). + */ + newmeta->rec_page = oldmeta->rec_page; + newmeta->re_pad = oldmeta->re_pad; + newmeta->re_len = oldmeta->re_len; + newmeta->cur_recno = oldmeta->cur_recno; + newmeta->first_recno = oldmeta->first_recno; + newmeta->start = oldmeta->start; + memmove(newmeta->dbmeta.uid, + oldmeta->dbmeta.uid, sizeof(oldmeta->dbmeta.uid)); + newmeta->dbmeta.flags = oldmeta->dbmeta.flags; + newmeta->dbmeta.record_count = 0; + newmeta->dbmeta.key_count = 0; + ZERO_LSN(newmeta->dbmeta.unused3); + + /* Update the version. */ + newmeta->dbmeta.version = 2; + + return (0); +} + +/* + * __qam_32_qammeta -- + * Upgrade the database from version 2 to version 3. + * + * PUBLIC: int __qam_32_qammeta __P((DB *, char *, u_int8_t *)); + */ +int +__qam_32_qammeta(dbp, real_name, buf) + DB *dbp; + char *real_name; + u_int8_t *buf; +{ + QMETA32 *newmeta; + QMETA31 *oldmeta; + + COMPQUIET(dbp, NULL); + COMPQUIET(real_name, NULL); + + newmeta = (QMETA32 *)buf; + oldmeta = (QMETA31 *)buf; + + /* + * Copy the fields to their new locations. + * We are dropping the first field so move + * from the top. + */ + newmeta->first_recno = oldmeta->first_recno; + newmeta->cur_recno = oldmeta->cur_recno; + newmeta->re_len = oldmeta->re_len; + newmeta->re_pad = oldmeta->re_pad; + newmeta->rec_page = oldmeta->rec_page; + newmeta->page_ext = 0; + /* cur_recno now points to the first free slot. */ + newmeta->cur_recno++; + if (newmeta->first_recno == 0) + newmeta->first_recno = 1; + + /* Update the version. */ + newmeta->dbmeta.version = 3; + + return (0); +} diff --git a/db/qam/qam_verify.c b/db/qam/qam_verify.c new file mode 100644 index 000000000..a9a467d67 --- /dev/null +++ b/db/qam/qam_verify.c @@ -0,0 +1,194 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: qam_verify.c,v 1.17 2000/12/12 17:39:35 bostic Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_verify.h" +#include "qam.h" +#include "db_ext.h" + +/* + * __qam_vrfy_meta -- + * Verify the queue-specific part of a metadata page. + * + * PUBLIC: int __qam_vrfy_meta __P((DB *, VRFY_DBINFO *, QMETA *, + * PUBLIC: db_pgno_t, u_int32_t)); + */ +int +__qam_vrfy_meta(dbp, vdp, meta, pgno, flags) + DB *dbp; + VRFY_DBINFO *vdp; + QMETA *meta; + db_pgno_t pgno; + u_int32_t flags; +{ + VRFY_PAGEINFO *pip; + int isbad, ret, t_ret; + + if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) + return (ret); + isbad = 0; + + /* + * Queue can't be used in subdatabases, so if this isn't set + * something very odd is going on. + */ + if (!F_ISSET(pip, VRFY_INCOMPLETE)) + EPRINT((dbp->dbenv, "Queue databases must be one-per-file.")); + + /* + * cur_recno/rec_page + * Cur_recno may be one beyond the end of the page and + * we start numbering from 1. + */ + if (vdp->last_pgno > 0 && meta->cur_recno > 0 && + meta->cur_recno - 1 > meta->rec_page * vdp->last_pgno) { + EPRINT((dbp->dbenv, + "Current recno %lu references record past last page number %lu", + meta->cur_recno, vdp->last_pgno)); + isbad = 1; + } + + /* + * re_len: If this is bad, we can't safely verify queue data pages, so + * return DB_VERIFY_FATAL + */ + if (ALIGN(meta->re_len + sizeof(QAMDATA) - 1, sizeof(u_int32_t)) * + meta->rec_page + sizeof(QPAGE) > dbp->pgsize) { + EPRINT((dbp->dbenv, + "Queue record length %lu impossibly high for page size and records per page", + meta->re_len)); + ret = DB_VERIFY_FATAL; + goto err; + } else { + vdp->re_len = meta->re_len; + vdp->rec_page = meta->rec_page; + } + +err: if ((t_ret = __db_vrfy_putpageinfo(vdp, pip)) != 0 && ret == 0) + ret = t_ret; + return (ret == 0 && isbad == 1 ? DB_VERIFY_BAD : ret); +} + +/* + * __qam_vrfy_data -- + * Verify a queue data page. + * + * PUBLIC: int __qam_vrfy_data __P((DB *, VRFY_DBINFO *, QPAGE *, + * PUBLIC: db_pgno_t, u_int32_t)); + */ +int +__qam_vrfy_data(dbp, vdp, h, pgno, flags) + DB *dbp; + VRFY_DBINFO *vdp; + QPAGE *h; + db_pgno_t pgno; + u_int32_t flags; +{ + DB fakedb; + struct __queue fakeq; + QAMDATA *qp; + db_recno_t i; + u_int8_t qflags; + + /* + * Not much to do here, except make sure that flags are reasonable. + * + * QAM_GET_RECORD assumes a properly initialized q_internal + * structure, however, and we don't have one, so we play + * some gross games to fake it out. + */ + fakedb.q_internal = &fakeq; + fakeq.re_len = vdp->re_len; + + for (i = 0; i < vdp->rec_page; i++) { + qp = QAM_GET_RECORD(&fakedb, h, i); + if ((u_int8_t *)qp >= (u_int8_t *)h + dbp->pgsize) { + EPRINT((dbp->dbenv, + "Queue record %lu extends past end of page %lu", + i, pgno)); + return (DB_VERIFY_BAD); + } + + qflags = qp->flags; + qflags &= !(QAM_VALID | QAM_SET); + if (qflags != 0) { + EPRINT((dbp->dbenv, + "Queue record %lu on page %lu has bad flags", + i, pgno)); + return (DB_VERIFY_BAD); + } + } + + return (0); +} + +/* + * __qam_vrfy_structure -- + * Verify a queue database structure, such as it is. + * + * PUBLIC: int __qam_vrfy_structure __P((DB *, VRFY_DBINFO *, u_int32_t)); + */ +int +__qam_vrfy_structure(dbp, vdp, flags) + DB *dbp; + VRFY_DBINFO *vdp; + u_int32_t flags; +{ + VRFY_PAGEINFO *pip; + db_pgno_t i; + int ret, isbad; + + isbad = 0; + + if ((ret = __db_vrfy_getpageinfo(vdp, PGNO_BASE_MD, &pip)) != 0) + return (ret); + + if (pip->type != P_QAMMETA) { + EPRINT((dbp->dbenv, + "Queue database has no meta page")); + isbad = 1; + goto err; + } + + if ((ret = __db_vrfy_pgset_inc(vdp->pgset, 0)) != 0) + goto err; + + for (i = 1; i <= vdp->last_pgno; i++) { + /* Send feedback to the application about our progress. */ + if (!LF_ISSET(DB_SALVAGE)) + __db_vrfy_struct_feedback(dbp, vdp); + + if ((ret = __db_vrfy_putpageinfo(vdp, pip)) != 0 || + (ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0) + return (ret); + if (!F_ISSET(pip, VRFY_IS_ALLZEROES) && + pip->type != P_QAMDATA) { + EPRINT((dbp->dbenv, + "Queue database page %lu of incorrect type %lu", + i, pip->type)); + isbad = 1; + goto err; + } else if ((ret = __db_vrfy_pgset_inc(vdp->pgset, i)) != 0) + goto err; + } + +err: if ((ret = __db_vrfy_putpageinfo(vdp, pip)) != 0) + return (ret); + return (isbad == 1 ? DB_VERIFY_BAD : 0); +} |