-
The memory pool fails to correct for 0-based page numbers when calculating
the end of the database file. The most likely symptom is that the db_dump
utility will fail, and no data corruption can occur.
*** mp/mp_fopen.c.orig Wed Dec 31 19:00:00 1969
--- mp/mp_fopen.c Mon Dec 21 11:12:23 1998
***************
*** 177,182 ****
--- 177,186 ----
last_pgno = mbytes * (MEGABYTE / pagesize);
last_pgno += bytes / pagesize;
+
+ /* Correction: page numbers are zero-based, not 1-based. */
+ if (last_pgno != 0)
+ --last_pgno;
/*
* Get the file id if we weren't given one. Generated file id's
-
The DB->put call, with the DB_NOOVERWRITE flag set, where the key already
exists in the tree, will overwrite the caller's data DBT with the value
already stored in the tree.
*** db/db_am.c.orig Wed Dec 31 19:00:00 1969
--- db/db_am.c Sat Jan 2 15:46:42 1999
***************
*** 347,352 ****
--- 347,353 ----
u_int32_t flags;
{
DBC *dbc;
+ DBT tdata;
int ret, t_ret;
DB_PANIC_CHECK(dbp);
***************
*** 360,369 ****
DEBUG_LWRITE(dbc, txn, "__db_put", key, data, flags);
! if (flags == DB_NOOVERWRITE &&
! (ret = dbc->c_get(dbc, key, data, DB_SET | DB_RMW)) == 0)
! ret = DB_KEYEXIST;
! else
ret = dbc->c_put(dbc, key, data, DB_KEYLAST);
if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0)
--- 361,380 ----
DEBUG_LWRITE(dbc, txn, "__db_put", key, data, flags);
! if (flags == DB_NOOVERWRITE) {
! /*
! * Set DB_DBT_USERMEM, this might be a threaded application and
! * the flags checking will catch us. We don't want the actual
! * data, so request a partial of length 0.
! */
! memset(&tdata, 0, sizeof(tdata));
! F_SET(&tdata, DB_DBT_USERMEM | DB_DBT_PARTIAL);
! if ((ret = dbc->c_get(dbc, key, &tdata, DB_SET | DB_RMW)) == 0)
! ret = DB_KEYEXIST;
! else
! ret = 0;
! }
! if (ret == 0)
ret = dbc->c_put(dbc, key, data, DB_KEYLAST);
if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0)
-
In the Berkeley DB Concurrent Access Methods Product (i.e., the
DB_INIT_CDB flag to the db_appinit interface), it was possible
for cursor close to not correctly release all acquired locks in
the B+tree access method.
*** db/db_am.c.orig Sun Jan 3 13:12:26 1999
--- db/db_am.c Tue Jan 5 09:39:30 1999
***************
*** 173,183 ****
ret = 0;
! /* Release the lock. */
! if (F_ISSET(dbc->dbp, DB_AM_CDB) && dbc->mylock != LOCK_INVALID) {
! ret = lock_put(dbc->dbp->dbenv->lk_info, dbc->mylock);
! dbc->mylock = LOCK_INVALID;
! }
/* Remove the cursor from the active queue. */
DB_THREAD_LOCK(dbp);
--- 173,183 ----
ret = 0;
! /*
! * We cannot release the lock until after we've called the
! * access method specific routine, since btrees may have pending
! * deletes.
! */
/* Remove the cursor from the active queue. */
DB_THREAD_LOCK(dbp);
***************
*** 188,193 ****
--- 188,199 ----
if ((t_ret = dbc->c_am_close(dbc)) != 0 && ret == 0)
t_ret = ret;
+ /* Release the lock. */
+ if (F_ISSET(dbc->dbp, DB_AM_CDB) && dbc->mylock != LOCK_INVALID) {
+ ret = lock_put(dbc->dbp->dbenv->lk_info, dbc->mylock);
+ dbc->mylock = LOCK_INVALID;
+ }
+
/* Clean up the cursor. */
dbc->flags = 0;
-
In Btree databases with large numbers of duplicates, it was possible to
incorrectly discard a page lock, leading to spurious error messages from
the underlying shared memory buffer cache.
*** btree/bt_cursor.c.orig Wed Dec 16 20:20:18 1998
--- btree/bt_cursor.c Wed Jan 6 16:02:36 1999
***************
*** 641,655 ****
}
/*
! * Move to the next item. If __bam_c_next returns DB_NOTFOUND
! * and we're doing an insert, reset the cursor back to the last
! * item and set the referenced memory location so callers know
! * to insert after the item, instead of before it. If not doing
! * an insert, return DB_NOTFOUND.
*/
! if ((ret = __bam_c_next(dbc, cp, 1)) != 0) {
! if (ret != DB_NOTFOUND || iflagp == NULL)
! return (ret);
goto use_last;
}
--- 641,655 ----
}
/*
! * Move to the next item. If we reach the end of the page and
! * we're doing an insert, set the cursor to the last item and
! * set the referenced memory location so callers know to insert
! * after the item, instead of before it. If not inserting, we
! * return DB_NOTFOUND.
*/
! if ((cp->indx += P_INDX) >= NUM_ENT(cp->page)) {
! if (iflagp == NULL)
! return (DB_NOTFOUND);
goto use_last;
}
-
Auto-configuration was incorrect when building threaded versions of the
Berkeley DB library on FreeBSD and Linux.
*** dist/configure.orig Mon Jan 11 09:11:38 1999
--- dist/configure Mon Jan 11 09:10:51 1999
***************
*** 3055,3063 ****
fi
case "$host_os" in
! freebsd*) CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS"
! LIBS="-lc_r";;
! irix*) CPPFLAGS="-D_SGI_MP_SOURCE $CPPFLAGS";;
osf*) CPPFLAGS="-D_REENTRANT $CPPFLAGS";;
solaris*) CPPFLAGS="-D_REENTRANT $CPPFLAGS"
LIBS="-lthread $LIBS";;
--- 3055,3063 ----
fi
case "$host_os" in
! freebsd*) CPPFLAGS="-D_THREAD_SAFE -pthread $CPPFLAGS";;
! irix*) CPPFLAGS="-D_SGI_MP_SOURCE $CPPFLAGS";;
! linux*) CPPFLAGS="-D_REENTRANT $CPPFLAGS";;
osf*) CPPFLAGS="-D_REENTRANT $CPPFLAGS";;
solaris*) CPPFLAGS="-D_REENTRANT $CPPFLAGS"
LIBS="-lthread $LIBS";;
-
There was a bug in returning partial records from an on-page duplicate in
hash databases.
*** hash/hash.c.orig Fri Dec 11 15:00:35 1998
--- hash/hash.c Tue Jan 12 22:18:49 1999
***************
*** 1027,1040 ****
ndx = hcp->dndx;
} else {
/*
! * Copy the DBT in case we are retrieving into
! * user memory and we need the parameters for
! * it.
*/
memcpy(&tmp_val, val, sizeof(*val));
! F_SET(&tmp_val, DB_DBT_PARTIAL);
! tmp_val.dlen = hcp->dup_len;
! tmp_val.doff = hcp->dup_off + sizeof(db_indx_t);
myval = &tmp_val;
}
--- 1027,1060 ----
ndx = hcp->dndx;
} else {
/*
! * Copy the DBT in case we are retrieving into user
! * memory and we need the parameters for it. If the
! * user requested a partial, then we need to adjust
! * the user's parameters to get the partial of the
! * duplicate which is itself a partial.
*/
memcpy(&tmp_val, val, sizeof(*val));
! if (F_ISSET(&tmp_val, DB_DBT_PARTIAL)) {
! /*
! * Take the user's length unless it would go
! * beyond the end of the duplicate.
! */
! if (tmp_val.doff + hcp->dup_off > hcp->dup_len)
! tmp_val.dlen = 0;
! else if (tmp_val.dlen + tmp_val.doff >
! hcp->dup_len)
! tmp_val.dlen =
! hcp->dup_len - tmp_val.doff;
!
! /*
! * Calculate the new offset.
! */
! tmp_val.doff += hcp->dup_off;
! } else {
! F_SET(&tmp_val, DB_DBT_PARTIAL);
! tmp_val.dlen = hcp->dup_len;
! tmp_val.doff = hcp->dup_off + sizeof(db_indx_t);
! }
myval = &tmp_val;
}
-
It was possible to overwrite the checkpoint LSN during checkpoint.
*** txn/txn.c.orig Sun Oct 25 21:14:49 1998
--- txn/txn.c Fri Jan 15 10:34:24 1999
***************
*** 674,680 ****
u_int32_t kbytes, minutes;
{
DB_LOG *dblp;
! DB_LSN ckp_lsn, last_ckp;
TXN_DETAIL *txnp;
time_t last_ckp_time, now;
u_int32_t kbytes_written;
--- 674,680 ----
u_int32_t kbytes, minutes;
{
DB_LOG *dblp;
! DB_LSN ckp_lsn, sync_lsn, last_ckp;
TXN_DETAIL *txnp;
time_t last_ckp_time, now;
u_int32_t kbytes_written;
***************
*** 750,757 ****
mgr->region->pending_ckp = ckp_lsn;
UNLOCK_TXNREGION(mgr);
if (mgr->dbenv->mp_info != NULL &&
! (ret = memp_sync(mgr->dbenv->mp_info, &ckp_lsn)) != 0) {
/*
* ret == DB_INCOMPLETE means that there are still buffers to
* flush, the checkpoint is not complete. Wait and try again.
--- 750,762 ----
mgr->region->pending_ckp = ckp_lsn;
UNLOCK_TXNREGION(mgr);
+ /*
+ * memp_sync may change the lsn you pass it, so don't pass it
+ * the actual ckp_lsn, pass it a temp instead.
+ */
+ sync_lsn = ckp_lsn;
if (mgr->dbenv->mp_info != NULL &&
! (ret = memp_sync(mgr->dbenv->mp_info, &sync_lsn)) != 0) {
/*
* ret == DB_INCOMPLETE means that there are still buffers to
* flush, the checkpoint is not complete. Wait and try again.
-
It was possible to run out of memory by using ever-increasing log
file numbers.
*** log/log_put.c.orig Tue Nov 3 10:02:06 1998
--- log/log_put.c Fri Jan 15 10:35:41 1999
***************
*** 157,162 ****
--- 157,164 ----
for (fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname);
fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) {
+ if (fnp->ref == 0) /* Entry not in use. */
+ continue;
memset(&t, 0, sizeof(t));
t.data = R_ADDR(dblp, fnp->name_off);
t.size = strlen(t.data) + 1;
*** log/log_register.c.orig Sun Sep 27 10:40:32 1998
--- log/log_register.c Fri Jan 15 10:35:52 1999
***************
*** 36,52 ****
{
DBT fid_dbt, r_name;
DB_LSN r_unused;
! FNAME *fnp;
size_t len;
! u_int32_t fid;
int inserted, ret;
char *fullname;
void *namep;
- fid = 0;
inserted = 0;
fullname = NULL;
! fnp = namep = NULL;
LOG_PANIC_CHECK(dblp);
--- 36,51 ----
{
DBT fid_dbt, r_name;
DB_LSN r_unused;
! FNAME *fnp, *reuse_fnp;
size_t len;
! u_int32_t maxid;
int inserted, ret;
char *fullname;
void *namep;
inserted = 0;
fullname = NULL;
! fnp = namep = reuse_fnp = NULL;
LOG_PANIC_CHECK(dblp);
***************
*** 65,88 ****
/*
* See if we've already got this file in the log, finding the
! * next-to-lowest file id currently in use as we do it.
*/
! for (fid = 1, fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname);
fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) {
! if (fid <= fnp->id)
! fid = fnp->id + 1;
if (!memcmp(dbp->fileid, fnp->ufid, DB_FILE_ID_LEN)) {
++fnp->ref;
- fid = fnp->id;
goto found;
}
}
! /* Allocate a new file name structure. */
! if ((ret = __db_shalloc(dblp->addr, sizeof(FNAME), 0, &fnp)) != 0)
goto err;
fnp->ref = 1;
- fnp->id = fid;
fnp->s_type = type;
memcpy(fnp->ufid, dbp->fileid, DB_FILE_ID_LEN);
--- 64,98 ----
/*
* See if we've already got this file in the log, finding the
! * (maximum+1) in-use file id and some available file id (if we
! * find an available fid, we'll use it, else we'll have to allocate
! * one after the maximum that we found).
*/
! for (maxid = 0, fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname);
fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) {
! if (fnp->ref == 0 && reuse_fnp == NULL) {
! /* Entry is not in use. */
! reuse_fnp = fnp;
! continue;
! }
if (!memcmp(dbp->fileid, fnp->ufid, DB_FILE_ID_LEN)) {
++fnp->ref;
goto found;
}
+ if (maxid <= fnp->id)
+ maxid = fnp->id + 1;
}
! /* Fill in fnp structure. */
!
! if (reuse_fnp != NULL) /* Reuse existing one. */
! fnp = reuse_fnp;
! else if ((ret = __db_shalloc(dblp->addr, sizeof(FNAME), 0, &fnp)) != 0)
goto err;
+ else /* Allocate a new one. */
+ fnp->id = maxid;
+
fnp->ref = 1;
fnp->s_type = type;
memcpy(fnp->ufid, dbp->fileid, DB_FILE_ID_LEN);
***************
*** 92,98 ****
fnp->name_off = R_OFFSET(dblp, namep);
memcpy(namep, name, len);
! SH_TAILQ_INSERT_HEAD(&dblp->lp->fq, fnp, q, __fname);
inserted = 1;
found: /* Log the registry. */
--- 102,110 ----
fnp->name_off = R_OFFSET(dblp, namep);
memcpy(namep, name, len);
! /* Only do the insert if we allocated a new fnp. */
! if (reuse_fnp == NULL)
! SH_TAILQ_INSERT_HEAD(&dblp->lp->fq, fnp, q, __fname);
inserted = 1;
found: /* Log the registry. */
***************
*** 103,111 ****
fid_dbt.data = dbp->fileid;
fid_dbt.size = DB_FILE_ID_LEN;
if ((ret = __log_register_log(dblp, NULL, &r_unused,
! 0, LOG_OPEN, &r_name, &fid_dbt, fid, type)) != 0)
goto err;
! if ((ret = __log_add_logid(dblp, dbp, fid)) != 0)
goto err;
}
--- 115,123 ----
fid_dbt.data = dbp->fileid;
fid_dbt.size = DB_FILE_ID_LEN;
if ((ret = __log_register_log(dblp, NULL, &r_unused,
! 0, LOG_OPEN, &r_name, &fid_dbt, fnp->id, type)) != 0)
goto err;
! if ((ret = __log_add_logid(dblp, dbp, fnp->id)) != 0)
goto err;
}
***************
*** 122,134 ****
__db_shalloc_free(dblp->addr, fnp);
}
UNLOCK_LOGREGION(dblp);
if (fullname != NULL)
__os_freestr(fullname);
- if (idp != NULL)
- *idp = fid;
return (ret);
}
--- 134,146 ----
__db_shalloc_free(dblp->addr, fnp);
}
+ if (idp != NULL)
+ *idp = fnp->id;
UNLOCK_LOGREGION(dblp);
if (fullname != NULL)
__os_freestr(fullname);
return (ret);
}
***************
*** 177,191 ****
/*
* If more than 1 reference, just decrement the reference and return.
! * Otherwise, free the unique file information, name and structure.
*/
! if (fnp->ref > 1)
! --fnp->ref;
! else {
__db_shalloc_free(dblp->addr, R_ADDR(dblp, fnp->name_off));
- SH_TAILQ_REMOVE(&dblp->lp->fq, fnp, q, __fname);
- __db_shalloc_free(dblp->addr, fnp);
- }
/*
* Remove from the process local table. If this operation is taking
--- 189,199 ----
/*
* If more than 1 reference, just decrement the reference and return.
! * Otherwise, free the name.
*/
! --fnp->ref;
! if (fnp->ref == 0)
__db_shalloc_free(dblp->addr, R_ADDR(dblp, fnp->name_off));
/*
* Remove from the process local table. If this operation is taking
-
It was possible for retrieval of specific duplicate records to cause a core
dump.
*** btree/bt_cursor.c.orig Wed Jan 6 21:38:45 1999
--- btree/bt_cursor.c Fri Jan 15 09:17:27 1999
***************
*** 58,63 ****
--- 58,72 ----
}
/* If the cursor references a deleted record. */
+ #undef IS_CUR_DELETED
+ #define IS_CUR_DELETED(cp) \
+ (((cp)->dpgno == PGNO_INVALID && \
+ B_DISSET(GET_BKEYDATA((cp)->page, \
+ (cp)->indx + O_INDX)->type)) || \
+ ((cp)->dpgno != PGNO_INVALID && \
+ B_DISSET(GET_BKEYDATA((cp)->page, (cp)->dindx)->type)))
+
+ /* If the cursor and index combination references a deleted record. */
#undef IS_DELETED
#define IS_DELETED(cp, indx) \
(((cp)->dpgno == PGNO_INVALID && \
***************
*** 486,491 ****
--- 495,506 ----
/* Search for a matching entry. */
if ((ret = __bam_dsearch(dbc, cp, data, NULL)) != 0)
goto err;
+
+ /* Ignore deleted entries. */
+ if (IS_CUR_DELETED(cp)) {
+ ret = DB_NOTFOUND;
+ goto err;
+ }
break;
case DB_SET_RANGE:
if ((ret = __bam_c_search(dbc, cp, key, flags, &exact)) != 0)
***************
*** 1070,1076 ****
return (ret);
/* If on an empty page or a deleted record, move to the next one. */
! if (NUM_ENT(cp->page) == 0 || IS_DELETED(cp, cp->indx))
if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
return (ret);
--- 1085,1091 ----
return (ret);
/* If on an empty page or a deleted record, move to the next one. */
! if (NUM_ENT(cp->page) == 0 || IS_CUR_DELETED(cp))
if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
return (ret);
***************
*** 1118,1124 ****
return (ret);
/* If on an empty page or a deleted record, move to the next one. */
! if (NUM_ENT(cp->page) == 0 || IS_DELETED(cp, cp->indx))
if ((ret = __bam_c_prev(dbc, cp)) != 0)
return (ret);
--- 1133,1139 ----
return (ret);
/* If on an empty page or a deleted record, move to the next one. */
! if (NUM_ENT(cp->page) == 0 || IS_CUR_DELETED(cp))
if ((ret = __bam_c_prev(dbc, cp)) != 0)
return (ret);
***************
*** 1556,1562 ****
return (0);
/* If it's a deleted record, go to the next valid record. */
! while (IS_DELETED(cp, cp->dindx))
if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
return (ret);
return (0);
--- 1571,1577 ----
return (0);
/* If it's a deleted record, go to the next valid record. */
! if (IS_CUR_DELETED(cp))
if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
return (ret);
return (0);
-
Locks waiting on an aborted lock were not correctly promoted at the time
of the abort.
*** lock/lock.c.orig Wed Dec 16 14:52:45 1998
--- lock/lock.c Fri Jan 22 15:10:34 1999
***************
*** 511,516 ****
--- 511,522 ----
LOCK_LOCKREGION(lt);
if (newl->status != DB_LSTAT_PENDING) {
+ /*
+ * If this lock errored due to a deadlock, then
+ * we have waiters that require promotion.
+ */
+ if (newl->status == DB_LSTAT_ABORTED)
+ (void)__lock_promote(lt, sh_obj);
/* Return to free list. */
__lock_checklocker(lt, newl, 0);
SH_TAILQ_INSERT_HEAD(&lrp->free_locks, newl, links,