1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996, 1997, 1998
5 *	Sleepycat Software.  All rights reserved.
6 */
7/*
8 * Copyright (c) 1995, 1996
9 *	Margo Seltzer.  All rights reserved.
10 */
11/*
12 * Copyright (c) 1995, 1996
13 *	The President and Fellows of Harvard University.  All rights reserved.
14 *
15 * This code is derived from software contributed to Berkeley by
16 * Margo Seltzer.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
20 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 *    notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 *    notice, this list of conditions and the following disclaimer in the
25 *    documentation and/or other materials provided with the distribution.
26 * 3. All advertising materials mentioning features or use of this software
27 *    must display the following acknowledgement:
28 *	This product includes software developed by the University of
29 *	California, Berkeley and its contributors.
30 * 4. Neither the name of the University nor the names of its contributors
31 *    may be used to endorse or promote products derived from this software
32 *    without specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 */
46
47#include "config.h"
48
49#ifndef lint
50static const char sccsid[] = "@(#)hash_rec.c	10.22 (Sleepycat) 10/21/98";
51#endif /* not lint */
52
53#ifndef NO_SYSTEM_INCLUDES
54#include <sys/types.h>
55
56#include <errno.h>
57#include <string.h>
58#endif
59
60#include "db_int.h"
61#include "shqueue.h"
62#include "db_page.h"
63#include "hash.h"
64#include "btree.h"
65#include "log.h"
66#include "common_ext.h"
67
68/*
69 * __ham_insdel_recover --
70 *
71 * PUBLIC: int __ham_insdel_recover
72 * PUBLIC:     __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
73 */
74int
75__ham_insdel_recover(logp, dbtp, lsnp, redo, info)
76	DB_LOG *logp;
77	DBT *dbtp;
78	DB_LSN *lsnp;
79	int redo;
80	void *info;
81{
82	__ham_insdel_args *argp;
83	DB *file_dbp;
84	DBC *dbc;
85	HASH_CURSOR *hcp;
86	DB_MPOOLFILE *mpf;
87	PAGE *pagep;
88	u_int32_t op;
89	int cmp_n, cmp_p, getmeta, ret;
90
91	getmeta = 0;
92	hcp = NULL;
93	REC_PRINT(__ham_insdel_print);
94	REC_INTRO(__ham_insdel_read);
95	hcp = (HASH_CURSOR *)dbc->internal;
96
97	ret = memp_fget(mpf, &argp->pgno, 0, &pagep);
98	if (ret != 0)
99		if (!redo) {
100			/*
101			 * We are undoing and the page doesn't exist.  That
102			 * is equivalent to having a pagelsn of 0, so we
103			 * would not have to undo anything.  In this case,
104			 * don't bother creating a page.
105			 */
106			goto done;
107		} else if ((ret = memp_fget(mpf, &argp->pgno,
108		    DB_MPOOL_CREATE, &pagep)) != 0)
109			goto out;
110
111
112	GET_META(file_dbp, hcp, ret);
113	if (ret != 0)
114		goto out;
115	getmeta = 1;
116
117	cmp_n = log_compare(lsnp, &LSN(pagep));
118	cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
119	/*
120	 * Two possible things going on:
121	 * redo a delete/undo a put: delete the item from the page.
122	 * redo a put/undo a delete: add the item to the page.
123	 * If we are undoing a delete, then the information logged is the
124	 * entire entry off the page, not just the data of a dbt.  In
125	 * this case, we want to copy it back onto the page verbatim.
126	 * We do this by calling __putitem with the type H_OFFPAGE instead
127	 * of H_KEYDATA.
128	 */
129	op = OPCODE_OF(argp->opcode);
130
131	if ((op == DELPAIR && cmp_n == 0 && !redo) ||
132	    (op == PUTPAIR && cmp_p == 0 && redo)) {
133		/*
134		 * Need to redo a PUT or undo a delete.  If we are undoing a
135		 * delete, we've got to restore the item back to its original
136		 * position.  That's a royal pain in the butt (because we do
137		 * not store item lengths on the page), but there's no choice.
138		 */
139		if (op != DELPAIR ||
140		    argp->ndx == (u_int32_t)H_NUMPAIRS(pagep)) {
141			__ham_putitem(pagep, &argp->key,
142			    !redo || PAIR_ISKEYBIG(argp->opcode) ?
143			    H_OFFPAGE : H_KEYDATA);
144			__ham_putitem(pagep, &argp->data,
145			    !redo || PAIR_ISDATABIG(argp->opcode) ?
146			    H_OFFPAGE : H_KEYDATA);
147		} else
148			(void) __ham_reputpair(pagep, hcp->hdr->pagesize,
149			    argp->ndx, &argp->key, &argp->data);
150
151		LSN(pagep) = redo ? *lsnp : argp->pagelsn;
152		if ((ret = __ham_put_page(file_dbp, pagep, 1)) != 0)
153			goto out;
154
155	} else if ((op == DELPAIR && cmp_p == 0 && redo)
156	    || (op == PUTPAIR && cmp_n == 0 && !redo)) {
157		/* Need to undo a put or redo a delete. */
158		__ham_dpair(file_dbp, pagep, argp->ndx);
159		LSN(pagep) = redo ? *lsnp : argp->pagelsn;
160		if ((ret = __ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0)
161			goto out;
162	} else
163		if ((ret = __ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0)
164			goto out;
165
166	/* Return the previous LSN. */
167done:	*lsnp = argp->prev_lsn;
168	ret = 0;
169
170out:	if (getmeta)
171		RELEASE_META(file_dbp, hcp);
172	REC_CLOSE;
173}
174
175/*
176 * __ham_newpage_recover --
177 *	This log message is used when we add/remove overflow pages.  This
178 *	message takes care of the pointer chains, not the data on the pages.
179 *
180 * PUBLIC: int __ham_newpage_recover
181 * PUBLIC:     __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
182 */
183int
184__ham_newpage_recover(logp, dbtp, lsnp, redo, info)
185	DB_LOG *logp;
186	DBT *dbtp;
187	DB_LSN *lsnp;
188	int redo;
189	void *info;
190{
191	__ham_newpage_args *argp;
192	DB *file_dbp;
193	DBC *dbc;
194	HASH_CURSOR *hcp;
195	DB_MPOOLFILE *mpf;
196	PAGE *pagep;
197	int cmp_n, cmp_p, change, getmeta, ret;
198
199	getmeta = 0;
200	hcp = NULL;
201	REC_PRINT(__ham_newpage_print);
202	REC_INTRO(__ham_newpage_read);
203	hcp = (HASH_CURSOR *)dbc->internal;
204
205	ret = memp_fget(mpf, &argp->new_pgno, 0, &pagep);
206	if (ret != 0)
207		if (!redo) {
208			/*
209			 * We are undoing and the page doesn't exist.  That
210			 * is equivalent to having a pagelsn of 0, so we
211			 * would not have to undo anything.  In this case,
212			 * don't bother creating a page.
213			 */
214			ret = 0;
215			goto ppage;
216		} else if ((ret = memp_fget(mpf, &argp->new_pgno,
217		    DB_MPOOL_CREATE, &pagep)) != 0)
218			goto out;
219
220	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
221	if (ret != 0)
222		goto out;
223	getmeta = 1;
224
225	/*
226	 * There are potentially three pages we need to check: the one
227	 * that we created/deleted, the one before it and the one after
228	 * it.
229	 */
230
231	cmp_n = log_compare(lsnp, &LSN(pagep));
232	cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
233	change = 0;
234
235	if ((cmp_p == 0 && redo && argp->opcode == PUTOVFL) ||
236	    (cmp_n == 0 && !redo && argp->opcode == DELOVFL)) {
237		/* Redo a create new page or undo a delete new page. */
238		P_INIT(pagep, file_dbp->pgsize, argp->new_pgno,
239		    argp->prev_pgno, argp->next_pgno, 0, P_HASH);
240		change = 1;
241	} else if ((cmp_p == 0 && redo && argp->opcode == DELOVFL) ||
242	    (cmp_n == 0 && !redo && argp->opcode == PUTOVFL)) {
243		/*
244		 * Redo a delete or undo a create new page.  All we
245		 * really need to do is change the LSN.
246		 */
247		change = 1;
248	}
249
250	if (!change) {
251		if ((ret = __ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0)
252			goto out;
253	} else {
254		LSN(pagep) = redo ? *lsnp : argp->pagelsn;
255		if ((ret = __ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0)
256			goto out;
257	}
258
259	/* Now do the prev page. */
260ppage:	if (argp->prev_pgno != PGNO_INVALID) {
261		ret = memp_fget(mpf, &argp->prev_pgno, 0, &pagep);
262
263		if (ret != 0)
264			if (!redo) {
265				/*
266				 * We are undoing and the page doesn't exist.
267				 * That is equivalent to having a pagelsn of 0,
268				 * so we would not have to undo anything.  In
269				 * this case, don't bother creating a page.
270				 */
271				ret = 0;
272				goto npage;
273			} else if ((ret =
274			    memp_fget(mpf, &argp->prev_pgno,
275			    DB_MPOOL_CREATE, &pagep)) != 0)
276				goto out;
277
278		cmp_n = log_compare(lsnp, &LSN(pagep));
279		cmp_p = log_compare(&LSN(pagep), &argp->prevlsn);
280		change = 0;
281
282		if ((cmp_p == 0 && redo && argp->opcode == PUTOVFL) ||
283		    (cmp_n == 0 && !redo && argp->opcode == DELOVFL)) {
284			/* Redo a create new page or undo a delete new page. */
285			pagep->next_pgno = argp->new_pgno;
286			change = 1;
287		} else if ((cmp_p == 0 && redo && argp->opcode == DELOVFL) ||
288		    (cmp_n == 0 && !redo && argp->opcode == PUTOVFL)) {
289			/* Redo a delete or undo a create new page. */
290			pagep->next_pgno = argp->next_pgno;
291			change = 1;
292		}
293
294		if (!change) {
295			if ((ret =
296			    __ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0)
297				goto out;
298		} else {
299			LSN(pagep) = redo ? *lsnp : argp->prevlsn;
300			if ((ret =
301			    __ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0)
302				goto out;
303		}
304	}
305
306	/* Now time to do the next page */
307npage:	if (argp->next_pgno != PGNO_INVALID) {
308		ret = memp_fget(mpf, &argp->next_pgno, 0, &pagep);
309
310		if (ret != 0)
311			if (!redo) {
312				/*
313				 * We are undoing and the page doesn't exist.
314				 * That is equivalent to having a pagelsn of 0,
315				 * so we would not have to undo anything.  In
316				 * this case, don't bother creating a page.
317				 */
318				goto done;
319			} else if ((ret =
320			    memp_fget(mpf, &argp->next_pgno,
321			    DB_MPOOL_CREATE, &pagep)) != 0)
322				goto out;
323
324		cmp_n = log_compare(lsnp, &LSN(pagep));
325		cmp_p = log_compare(&LSN(pagep), &argp->nextlsn);
326		change = 0;
327
328		if ((cmp_p == 0 && redo && argp->opcode == PUTOVFL) ||
329		    (cmp_n == 0 && !redo && argp->opcode == DELOVFL)) {
330			/* Redo a create new page or undo a delete new page. */
331			pagep->prev_pgno = argp->new_pgno;
332			change = 1;
333		} else if ((cmp_p == 0 && redo && argp->opcode == DELOVFL) ||
334		    (cmp_n == 0 && !redo && argp->opcode == PUTOVFL)) {
335			/* Redo a delete or undo a create new page. */
336			pagep->prev_pgno = argp->prev_pgno;
337			change = 1;
338		}
339
340		if (!change) {
341			if ((ret =
342			    __ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0)
343				goto out;
344		} else {
345			LSN(pagep) = redo ? *lsnp : argp->nextlsn;
346			if ((ret =
347			    __ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0)
348				goto out;
349		}
350	}
351done:	*lsnp = argp->prev_lsn;
352	ret = 0;
353
354out:	if (getmeta)
355		RELEASE_META(file_dbp, hcp);
356	REC_CLOSE;
357}
358
359
360/*
361 * __ham_replace_recover --
362 *	This log message refers to partial puts that are local to a single
363 *	page.  You can think of them as special cases of the more general
364 *	insdel log message.
365 *
366 * PUBLIC: int __ham_replace_recover
367 * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
368 */
369int
370__ham_replace_recover(logp, dbtp, lsnp, redo, info)
371	DB_LOG *logp;
372	DBT *dbtp;
373	DB_LSN *lsnp;
374	int redo;
375	void *info;
376{
377	__ham_replace_args *argp;
378	DB *file_dbp;
379	DBC *dbc;
380	HASH_CURSOR *hcp;
381	DB_MPOOLFILE *mpf;
382	DBT dbt;
383	PAGE *pagep;
384	int32_t grow;
385	int change, cmp_n, cmp_p, getmeta, ret;
386	u_int8_t *hk;
387
388	getmeta = 0;
389	hcp = NULL;
390	REC_PRINT(__ham_replace_print);
391	REC_INTRO(__ham_replace_read);
392	hcp = (HASH_CURSOR *)dbc->internal;
393
394	ret = memp_fget(mpf, &argp->pgno, 0, &pagep);
395	if (ret != 0)
396		if (!redo) {
397			/*
398			 * We are undoing and the page doesn't exist.  That
399			 * is equivalent to having a pagelsn of 0, so we
400			 * would not have to undo anything.  In this case,
401			 * don't bother creating a page.
402			 */
403			goto done;
404		} else if ((ret = memp_fget(mpf, &argp->pgno,
405		    DB_MPOOL_CREATE, &pagep)) != 0)
406			goto out;
407
408	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
409	if (ret != 0)
410		goto out;
411	getmeta = 1;
412
413	cmp_n = log_compare(lsnp, &LSN(pagep));
414	cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
415
416	if (cmp_p == 0 && redo) {
417		change = 1;
418		/* Reapply the change as specified. */
419		dbt.data = argp->newitem.data;
420		dbt.size = argp->newitem.size;
421		grow = argp->newitem.size - argp->olditem.size;
422		LSN(pagep) = *lsnp;
423	} else if (cmp_n == 0 && !redo) {
424		change = 1;
425		/* Undo the already applied change. */
426		dbt.data = argp->olditem.data;
427		dbt.size = argp->olditem.size;
428		grow = argp->olditem.size - argp->newitem.size;
429		LSN(pagep) = argp->pagelsn;
430	} else {
431		change = 0;
432		grow = 0;
433	}
434
435	if (change) {
436		__ham_onpage_replace(pagep,
437		    file_dbp->pgsize, argp->ndx, argp->off, grow, &dbt);
438		if (argp->makedup) {
439			hk = P_ENTRY(pagep, argp->ndx);
440			if (redo)
441				HPAGE_PTYPE(hk) = H_DUPLICATE;
442			else
443				HPAGE_PTYPE(hk) = H_KEYDATA;
444		}
445	}
446
447	if ((ret = __ham_put_page(file_dbp, pagep, change)) != 0)
448		goto out;
449
450done:	*lsnp = argp->prev_lsn;
451	ret = 0;
452
453out:	if (getmeta)
454		RELEASE_META(file_dbp, hcp);
455	REC_CLOSE;
456}
457
458/*
459 * __ham_newpgno_recover --
460 *	This log message is used when allocating or deleting an overflow
461 *	page.  It takes care of modifying the meta data.
462 *
463 * PUBLIC: int __ham_newpgno_recover
464 * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
465 */
466int
467__ham_newpgno_recover(logp, dbtp, lsnp, redo, info)
468	DB_LOG *logp;
469	DBT *dbtp;
470	DB_LSN *lsnp;
471	int redo;
472	void *info;
473{
474	__ham_newpgno_args *argp;
475	DB *file_dbp;
476	DBC *dbc;
477	HASH_CURSOR *hcp;
478	DB_MPOOLFILE *mpf;
479	PAGE *pagep;
480	int change, cmp_n, cmp_p, getmeta, ret;
481
482	getmeta = 0;
483	hcp = NULL;
484	REC_PRINT(__ham_newpgno_print);
485	REC_INTRO(__ham_newpgno_read);
486	hcp = (HASH_CURSOR *)dbc->internal;
487
488	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
489	if (ret != 0)
490		goto out;
491	getmeta = 1;
492
493	/*
494	 * There are two phases to the recovery here.  First we need
495	 * to update the meta data; then we need to update the page.
496	 * We'll do the meta-data first.
497	 */
498	cmp_n = log_compare(lsnp, &hcp->hdr->lsn);
499	cmp_p = log_compare(&hcp->hdr->lsn, &argp->metalsn);
500
501	change = 0;
502	if ((cmp_p == 0 && redo && argp->opcode == ALLOCPGNO) ||
503	    (cmp_n == 0 && !redo && argp->opcode == DELPGNO)) {
504		/* Need to redo an allocation or undo a deletion. */
505		hcp->hdr->last_freed = argp->free_pgno;
506		if (redo && argp->old_pgno != 0) /* Must be ALLOCPGNO */
507			hcp->hdr->spares[hcp->hdr->ovfl_point]++;
508		change = 1;
509	} else if (cmp_p == 0 && redo && argp->opcode == DELPGNO) {
510		/* Need to redo a deletion */
511		hcp->hdr->last_freed = argp->pgno;
512		change = 1;
513	} else if (cmp_n == 0 && !redo && argp->opcode == ALLOCPGNO) {
514		/* undo an allocation. */
515		if (argp->old_pgno == 0)
516			hcp->hdr->last_freed = argp->pgno;
517		else {
518			hcp->hdr->spares[hcp->hdr->ovfl_point]--;
519			hcp->hdr->last_freed = 0;
520		}
521		change = 1;
522	}
523	if (change) {
524		hcp->hdr->lsn = redo ? *lsnp : argp->metalsn;
525		F_SET(hcp, H_DIRTY);
526	}
527
528
529	/* Now check the newly allocated/freed page. */
530	ret = memp_fget(mpf, &argp->pgno, 0, &pagep);
531
532	if (ret != 0)
533		if (!redo) {
534			/*
535			 * We are undoing and the page doesn't exist.  That
536			 * is equivalent to having a pagelsn of 0, so we
537			 * would not have to undo anything.  In this case,
538			 * don't bother creating a page.
539			 */
540			goto done;
541		} else if ((ret = memp_fget(mpf, &argp->pgno,
542		    DB_MPOOL_CREATE, &pagep)) != 0)
543			goto out;
544
545	cmp_n = log_compare(lsnp, &LSN(pagep));
546	cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
547
548	change = 0;
549	if (cmp_p == 0 && redo && argp->opcode == ALLOCPGNO) {
550		/* Need to redo an allocation. */
551		P_INIT(pagep, file_dbp->pgsize, argp->pgno, PGNO_INVALID,
552		    PGNO_INVALID, 0, argp->new_type);
553		change = 1;
554	} else if (cmp_n == 0 && !redo && argp->opcode == DELPGNO) {
555		/* Undoing a delete. */
556		P_INIT(pagep, file_dbp->pgsize, argp->pgno, PGNO_INVALID,
557		    argp->old_pgno, 0, argp->old_type);
558		change = 1;
559	} else if ((cmp_p == 0 && redo && argp->opcode == DELPGNO) ||
560	    (cmp_n == 0 && !redo && argp->opcode == ALLOCPGNO)) {
561		/* Need to redo a deletion or undo an allocation. */
562		NEXT_PGNO(pagep) = argp->free_pgno;
563		TYPE(pagep) = P_INVALID;
564		change = 1;
565	}
566	if (change)
567		LSN(pagep) = redo ? *lsnp : argp->pagelsn;
568
569	if ((ret = __ham_put_page(file_dbp, pagep, change)) != 0)
570		goto out;
571
572done:	*lsnp = argp->prev_lsn;
573	ret = 0;
574
575out:	if (getmeta)
576		RELEASE_META(file_dbp, hcp);
577	REC_CLOSE;
578
579}
580
581/*
582 * __ham_splitmeta_recover --
583 *	This is the meta-data part of the split.  Records the new and old
584 *	bucket numbers and the new/old mask information.
585 *
586 * PUBLIC: int __ham_splitmeta_recover
587 * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
588 */
589int
590__ham_splitmeta_recover(logp, dbtp, lsnp, redo, info)
591	DB_LOG *logp;
592	DBT *dbtp;
593	DB_LSN *lsnp;
594	int redo;
595	void *info;
596{
597	__ham_splitmeta_args *argp;
598	DB *file_dbp;
599	DBC *dbc;
600	HASH_CURSOR *hcp;
601	DB_MPOOLFILE *mpf;
602	int change, cmp_n, cmp_p, getmeta, ret;
603	u_int32_t pow;
604
605	getmeta = 0;
606	hcp = NULL;
607	REC_PRINT(__ham_splitmeta_print);
608	REC_INTRO(__ham_splitmeta_read);
609	hcp = (HASH_CURSOR *)dbc->internal;
610
611	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
612	if (ret != 0)
613		goto out;
614	getmeta = 1;
615
616	/*
617	 * There are two phases to the recovery here.  First we need
618	 * to update the meta data; then we need to update the page.
619	 * We'll do the meta-data first.
620	 */
621	cmp_n = log_compare(lsnp, &hcp->hdr->lsn);
622	cmp_p = log_compare(&hcp->hdr->lsn, &argp->metalsn);
623
624	change = 0;
625	if (cmp_p == 0 && redo) {
626		/* Need to redo the split information. */
627		hcp->hdr->max_bucket = argp->bucket + 1;
628		pow = __db_log2(hcp->hdr->max_bucket + 1);
629		if (pow > hcp->hdr->ovfl_point) {
630			hcp->hdr->spares[pow] =
631				hcp->hdr->spares[hcp->hdr->ovfl_point];
632			hcp->hdr->ovfl_point = pow;
633		}
634		if (hcp->hdr->max_bucket > hcp->hdr->high_mask) {
635			hcp->hdr->low_mask = hcp->hdr->high_mask;
636			hcp->hdr->high_mask =
637			    hcp->hdr->max_bucket | hcp->hdr->low_mask;
638		}
639		change = 1;
640	} else if (cmp_n == 0 && !redo) {
641		/* Need to undo the split information. */
642		hcp->hdr->max_bucket = argp->bucket;
643		hcp->hdr->ovfl_point = argp->ovflpoint;
644		hcp->hdr->spares[hcp->hdr->ovfl_point] = argp->spares;
645		pow = 1 << __db_log2(hcp->hdr->max_bucket + 1);
646		hcp->hdr->high_mask = pow - 1;
647		hcp->hdr->low_mask = (pow >> 1) - 1;
648		change = 1;
649	}
650	if (change) {
651		hcp->hdr->lsn = redo ? *lsnp : argp->metalsn;
652		F_SET(hcp, H_DIRTY);
653	}
654
655done:	*lsnp = argp->prev_lsn;
656	ret = 0;
657
658out:	if (getmeta)
659		RELEASE_META(file_dbp, hcp);
660	REC_CLOSE;
661}
662
663/*
664 * __ham_splitdata_recover --
665 *
666 * PUBLIC: int __ham_splitdata_recover
667 * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
668 */
669int
670__ham_splitdata_recover(logp, dbtp, lsnp, redo, info)
671	DB_LOG *logp;
672	DBT *dbtp;
673	DB_LSN *lsnp;
674	int redo;
675	void *info;
676{
677	__ham_splitdata_args *argp;
678	DB *file_dbp;
679	DBC *dbc;
680	HASH_CURSOR *hcp;
681	DB_MPOOLFILE *mpf;
682	PAGE *pagep;
683	int change, cmp_n, cmp_p, getmeta, ret;
684
685	getmeta = 0;
686	hcp = NULL;
687	REC_PRINT(__ham_splitdata_print);
688	REC_INTRO(__ham_splitdata_read);
689	hcp = (HASH_CURSOR *)dbc->internal;
690
691	ret = memp_fget(mpf, &argp->pgno, 0, &pagep);
692	if (ret != 0)
693		if (!redo) {
694			/*
695			 * We are undoing and the page doesn't exist.  That
696			 * is equivalent to having a pagelsn of 0, so we
697			 * would not have to undo anything.  In this case,
698			 * don't bother creating a page.
699			 */
700			goto done;
701		} else if ((ret = memp_fget(mpf, &argp->pgno,
702		    DB_MPOOL_CREATE, &pagep)) != 0)
703			goto out;
704
705	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
706	if (ret != 0)
707		goto out;
708	getmeta = 1;
709
710	cmp_n = log_compare(lsnp, &LSN(pagep));
711	cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
712
713	/*
714	 * There are two types of log messages here, one for the old page
715	 * and one for the new pages created.  The original image in the
716	 * SPLITOLD record is used for undo.  The image in the SPLITNEW
717	 * is used for redo.  We should never have a case where there is
718	 * a redo operation and the SPLITOLD record is on disk, but not
719	 * the SPLITNEW record.  Therefore, we only have work to do when
720	 * redo NEW messages and undo OLD messages, but we have to update
721	 * LSNs in both cases.
722	 */
723	change = 0;
724	if (cmp_p == 0 && redo) {
725		if (argp->opcode == SPLITNEW)
726			/* Need to redo the split described. */
727			memcpy(pagep, argp->pageimage.data,
728			    argp->pageimage.size);
729		LSN(pagep) = *lsnp;
730		change = 1;
731	} else if (cmp_n == 0 && !redo) {
732		if (argp->opcode == SPLITOLD) {
733			/* Put back the old image. */
734			memcpy(pagep, argp->pageimage.data,
735			    argp->pageimage.size);
736		} else
737			P_INIT(pagep, file_dbp->pgsize, argp->pgno,
738			    PGNO_INVALID, PGNO_INVALID, 0, P_HASH);
739		LSN(pagep) = argp->pagelsn;
740		change = 1;
741	}
742	if ((ret = __ham_put_page(file_dbp, pagep, change)) != 0)
743		goto out;
744
745done:	*lsnp = argp->prev_lsn;
746	ret = 0;
747
748out:	if (getmeta)
749		RELEASE_META(file_dbp, hcp);
750	REC_CLOSE;
751}
752
753/*
754 * __ham_ovfl_recover --
755 *	This message is generated when we initialize a set of overflow pages.
756 *
757 * PUBLIC: int __ham_ovfl_recover
758 * PUBLIC:     __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
759 */
760int
761__ham_ovfl_recover(logp, dbtp, lsnp, redo, info)
762	DB_LOG *logp;
763	DBT *dbtp;
764	DB_LSN *lsnp;
765	int redo;
766	void *info;
767{
768	__ham_ovfl_args *argp;
769	DB *file_dbp;
770	DBC *dbc;
771	HASH_CURSOR *hcp;
772	DB_MPOOLFILE *mpf;
773	PAGE *pagep;
774	db_pgno_t max_pgno, pgno;
775	int cmp_n, cmp_p, getmeta, ret;
776
777	getmeta = 0;
778	hcp = NULL;
779	REC_PRINT(__ham_ovfl_print);
780	REC_INTRO(__ham_ovfl_read);
781	hcp = (HASH_CURSOR *)dbc->internal;
782
783	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
784	if (ret != 0)
785		goto out;
786	getmeta = 1;
787
788	cmp_n = log_compare(lsnp, &hcp->hdr->lsn);
789	cmp_p = log_compare(&hcp->hdr->lsn, &argp->metalsn);
790
791	if (cmp_p == 0 && redo) {
792		/* Redo the allocation. */
793		hcp->hdr->last_freed = argp->start_pgno;
794		hcp->hdr->spares[argp->ovflpoint] += argp->npages;
795		hcp->hdr->lsn = *lsnp;
796		F_SET(hcp, H_DIRTY);
797	} else if (cmp_n == 0 && !redo) {
798		hcp->hdr->last_freed = argp->free_pgno;
799		hcp->hdr->spares[argp->ovflpoint] -= argp->npages;
800		hcp->hdr->lsn = argp->metalsn;
801		F_SET(hcp, H_DIRTY);
802	}
803
804	max_pgno = argp->start_pgno + argp->npages - 1;
805	ret = 0;
806	for (pgno = argp->start_pgno; pgno <= max_pgno; pgno++) {
807		if ((ret = memp_fget(mpf, &pgno, 0, &pagep)) != 0) {
808			if (!redo) {
809				ret = 0;
810				continue;
811			}
812			if ((ret = memp_fget(mpf,
813			    &pgno, DB_MPOOL_CREATE, &pagep)) != 0)
814				goto out;
815		}
816		if (redo && log_compare((const DB_LSN *)lsnp,
817		    (const DB_LSN *)&LSN(pagep)) > 0) {
818			P_INIT(pagep, file_dbp->pgsize, pgno, PGNO_INVALID,
819			    pgno == max_pgno ? argp->free_pgno : pgno + 1,
820			    0, P_HASH);
821			LSN(pagep) = *lsnp;
822			ret = __ham_put_page(file_dbp, pagep, 1);
823		} else if (!redo) {
824			ZERO_LSN(pagep->lsn);
825			ret = __ham_put_page(file_dbp, pagep, 1);
826		} else
827			ret = __ham_put_page(file_dbp, pagep, 0);
828		if (ret)
829			goto out;
830	}
831
832done:	*lsnp = argp->prev_lsn;
833	ret = 0;
834
835out:	if (getmeta)
836		RELEASE_META(file_dbp, hcp);
837	REC_CLOSE;
838}
839
840/*
841 * __ham_copypage_recover --
842 *	Recovery function for copypage.
843 *
844 * PUBLIC: int __ham_copypage_recover
845 * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
846 */
847int
848__ham_copypage_recover(logp, dbtp, lsnp, redo, info)
849	DB_LOG *logp;
850	DBT *dbtp;
851	DB_LSN *lsnp;
852	int redo;
853	void *info;
854{
855	__ham_copypage_args *argp;
856	DB *file_dbp;
857	DBC *dbc;
858	HASH_CURSOR *hcp;
859	DB_MPOOLFILE *mpf;
860	PAGE *pagep;
861	int cmp_n, cmp_p, getmeta, modified, ret;
862
863	getmeta = 0;
864	hcp = NULL;
865	REC_PRINT(__ham_copypage_print);
866	REC_INTRO(__ham_copypage_read);
867	hcp = (HASH_CURSOR *)dbc->internal;
868
869	GET_META(file_dbp, (HASH_CURSOR *)dbc->internal, ret);
870	if (ret != 0)
871		goto out;
872	getmeta = 1;
873	modified = 0;
874
875	/* This is the bucket page. */
876	ret = memp_fget(mpf, &argp->pgno, 0, &pagep);
877	if (ret != 0)
878		if (!redo) {
879			/*
880			 * We are undoing and the page doesn't exist.  That
881			 * is equivalent to having a pagelsn of 0, so we
882			 * would not have to undo anything.  In this case,
883			 * don't bother creating a page.
884			 */
885			ret = 0;
886			goto donext;
887		} else if ((ret = memp_fget(mpf, &argp->pgno,
888		    DB_MPOOL_CREATE, &pagep)) != 0)
889			goto out;
890
891	cmp_n = log_compare(lsnp, &LSN(pagep));
892	cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
893
894	if (cmp_p == 0 && redo) {
895		/* Need to redo update described. */
896		memcpy(pagep, argp->page.data, argp->page.size);
897		LSN(pagep) = *lsnp;
898		modified = 1;
899	} else if (cmp_n == 0 && !redo) {
900		/* Need to undo update described. */
901		P_INIT(pagep, hcp->hdr->pagesize, argp->pgno, PGNO_INVALID,
902		    argp->next_pgno, 0, P_HASH);
903		LSN(pagep) = argp->pagelsn;
904		modified = 1;
905	}
906	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
907		goto out;
908
909	/* Now fix up the "next" page. */
910donext:	ret = memp_fget(mpf, &argp->next_pgno, 0, &pagep);
911	if (ret != 0)
912		if (!redo) {
913			/*
914			 * We are undoing and the page doesn't exist.  That
915			 * is equivalent to having a pagelsn of 0, so we
916			 * would not have to undo anything.  In this case,
917			 * don't bother creating a page.
918			 */
919			ret = 0;
920			goto do_nn;
921		} else if ((ret = memp_fget(mpf, &argp->next_pgno,
922		    DB_MPOOL_CREATE, &pagep)) != 0)
923			goto out;
924
925	/* There is nothing to do in the REDO case; only UNDO. */
926
927	cmp_n = log_compare(lsnp, &LSN(pagep));
928	if (cmp_n == 0 && !redo) {
929		/* Need to undo update described. */
930		memcpy(pagep, argp->page.data, argp->page.size);
931		modified = 1;
932	}
933	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
934		goto out;
935
936	/* Now fix up the next's next page. */
937do_nn:	if (argp->nnext_pgno == PGNO_INVALID)
938		goto done;
939
940	ret = memp_fget(mpf, &argp->nnext_pgno, 0, &pagep);
941	if (ret != 0)
942		if (!redo) {
943			/*
944			 * We are undoing and the page doesn't exist.  That
945			 * is equivalent to having a pagelsn of 0, so we
946			 * would not have to undo anything.  In this case,
947			 * don't bother creating a page.
948			 */
949			goto done;
950		} else if ((ret = memp_fget(mpf, &argp->nnext_pgno,
951		    DB_MPOOL_CREATE, &pagep)) != 0)
952			goto out;
953
954	cmp_n = log_compare(lsnp, &LSN(pagep));
955	cmp_p = log_compare(&LSN(pagep), &argp->nnextlsn);
956
957	if (cmp_p == 0 && redo) {
958		/* Need to redo update described. */
959		PREV_PGNO(pagep) = argp->pgno;
960		LSN(pagep) = *lsnp;
961		modified = 1;
962	} else if (cmp_n == 0 && !redo) {
963		/* Need to undo update described. */
964		PREV_PGNO(pagep) = argp->next_pgno;
965		LSN(pagep) = argp->nnextlsn;
966		modified = 1;
967	}
968	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
969		goto out;
970
971done:	*lsnp = argp->prev_lsn;
972	ret = 0;
973
974out:	if (getmeta)
975		RELEASE_META(file_dbp, hcp);
976	REC_CLOSE;
977}
978