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
50 static 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  */
74 int
__ham_insdel_recover(logp,dbtp,lsnp,redo,info)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. */
167 done:	*lsnp = argp->prev_lsn;
168 	ret = 0;
169 
170 out:	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  */
183 int
__ham_newpage_recover(logp,dbtp,lsnp,redo,info)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. */
260 ppage:	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 */
307 npage:	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 	}
351 done:	*lsnp = argp->prev_lsn;
352 	ret = 0;
353 
354 out:	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  */
369 int
__ham_replace_recover(logp,dbtp,lsnp,redo,info)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 
450 done:	*lsnp = argp->prev_lsn;
451 	ret = 0;
452 
453 out:	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  */
466 int
__ham_newpgno_recover(logp,dbtp,lsnp,redo,info)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 
572 done:	*lsnp = argp->prev_lsn;
573 	ret = 0;
574 
575 out:	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  */
589 int
__ham_splitmeta_recover(logp,dbtp,lsnp,redo,info)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 
655 done:	*lsnp = argp->prev_lsn;
656 	ret = 0;
657 
658 out:	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  */
669 int
__ham_splitdata_recover(logp,dbtp,lsnp,redo,info)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 
745 done:	*lsnp = argp->prev_lsn;
746 	ret = 0;
747 
748 out:	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  */
760 int
__ham_ovfl_recover(logp,dbtp,lsnp,redo,info)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 
832 done:	*lsnp = argp->prev_lsn;
833 	ret = 0;
834 
835 out:	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  */
847 int
__ham_copypage_recover(logp,dbtp,lsnp,redo,info)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. */
910 donext:	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. */
937 do_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 
971 done:	*lsnp = argp->prev_lsn;
972 	ret = 0;
973 
974 out:	if (getmeta)
975 		RELEASE_META(file_dbp, hcp);
976 	REC_CLOSE;
977 }
978