00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1996, 1997, 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 /* 00008 * Copyright (c) 1995, 1996 00009 * Margo Seltzer. All rights reserved. 00010 */ 00011 /* 00012 * Copyright (c) 1995, 1996 00013 * The President and Fellows of Harvard University. All rights reserved. 00014 * 00015 * This code is derived from software contributed to Berkeley by 00016 * Margo Seltzer. 00017 * 00018 * Redistribution and use in source and binary forms, with or without 00019 * modification, are permitted provided that the following conditions 00020 * are met: 00021 * 1. Redistributions of source code must retain the above copyright 00022 * notice, this list of conditions and the following disclaimer. 00023 * 2. Redistributions in binary form must reproduce the above copyright 00024 * notice, this list of conditions and the following disclaimer in the 00025 * documentation and/or other materials provided with the distribution. 00026 * 3. Neither the name of the University nor the names of its contributors 00027 * may be used to endorse or promote products derived from this software 00028 * without specific prior written permission. 00029 * 00030 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 00031 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00032 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00033 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 00034 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00035 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00036 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00037 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00038 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00039 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00040 * SUCH DAMAGE. 00041 */ 00042 00043 #include "config.h" 00044 00045 #ifndef lint 00046 static const char revid[] = "$Id: hash__rec_8c-source.html,v 1.1 2008年06月08日 10:19:37 sebdiaz Exp $"; 00047 #endif /* not lint */ 00048 00049 #ifndef NO_SYSTEM_INCLUDES 00050 #include <sys/types.h> 00051 00052 #include <errno.h> 00053 #include <string.h> 00054 #endif 00055 00056 #include "db_int.h" 00057 #include "db_page.h" 00058 #include "db_shash.h" 00059 #include "btree.h" 00060 #include "hash.h" 00061 #include "lock.h" 00062 #include "log.h" 00063 #include "mp.h" 00064 00065 static int __ham_alloc_pages __P((DB *, __ham_groupalloc_args *)); 00066 static int __ham_free_pages __P((DB *, DBMETA *, __ham_groupalloc_args *)); 00067 00068 /* 00069 * CDB___ham_insdel_recover -- 00070 * 00071 * PUBLIC: int CDB___ham_insdel_recover 00072 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00073 */ 00074 int 00075 CDB___ham_insdel_recover(dbenv, dbtp, lsnp, op, info) 00076 DB_ENV *dbenv; 00077 DBT *dbtp; 00078 DB_LSN *lsnp; 00079 db_recops op; 00080 void *info; 00081 { 00082 __ham_insdel_args *argp; 00083 DB *file_dbp; 00084 DBC *dbc; 00085 DB_MPOOLFILE *mpf; 00086 PAGE *pagep; 00087 u_int32_t opcode; 00088 int cmp_n, cmp_p, getmeta, ret, type; 00089 00090 COMPQUIET(info, NULL); 00091 00092 getmeta = 0; 00093 REC_PRINT(CDB___ham_insdel_print); 00094 REC_INTRO(CDB___ham_insdel_read, 1); 00095 00096 if ((ret = CDB_memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) { 00097 if (DB_UNDO(op)) { 00098 /* 00099 * We are undoing and the page doesn't exist. That 00100 * is equivalent to having a pagelsn of 0, so we 00101 * would not have to undo anything. In this case, 00102 * don't bother creating a page. 00103 */ 00104 goto done; 00105 } else if ((ret = CDB_memp_fget(mpf, &argp->pgno, 00106 DB_MPOOL_CREATE, &pagep)) != 0) 00107 goto out; 00108 } 00109 00110 if ((ret = CDB___ham_get_meta(dbc)) != 0) 00111 goto out; 00112 getmeta = 1; 00113 00114 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00115 cmp_p = CDB_log_compare(&LSN(pagep), &argp->pagelsn); 00116 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->pagelsn); 00117 /* 00118 * Two possible things going on: 00119 * redo a delete/undo a put: delete the item from the page. 00120 * redo a put/undo a delete: add the item to the page. 00121 * If we are undoing a delete, then the information logged is the 00122 * entire entry off the page, not just the data of a dbt. In 00123 * this case, we want to copy it back onto the page verbatim. 00124 * We do this by calling __putitem with the type H_OFFPAGE instead 00125 * of H_KEYDATA. 00126 */ 00127 opcode = OPCODE_OF(argp->opcode); 00128 00129 if ((opcode == DELPAIR && cmp_n == 0 && DB_UNDO(op)) || 00130 (opcode == PUTPAIR && cmp_p == 0 && DB_REDO(op))) { 00131 /* 00132 * Need to redo a PUT or undo a delete. If we are undoing a 00133 * delete, we've got to restore the item back to its original 00134 * position. That's a royal pain in the butt (because we do 00135 * not store item lengths on the page), but there's no choice. 00136 */ 00137 if (opcode != DELPAIR || 00138 argp->ndx == (u_int32_t)NUM_ENT(pagep)) { 00139 CDB___ham_putitem(pagep, &argp->key, 00140 DB_UNDO(op) || PAIR_ISKEYBIG(argp->opcode) ? 00141 H_OFFPAGE : H_KEYDATA); 00142 00143 if (PAIR_ISDATADUP(argp->opcode)) 00144 type = H_DUPLICATE; 00145 else if (DB_UNDO(op) || PAIR_ISDATABIG(argp->opcode)) 00146 type = H_OFFPAGE; 00147 else 00148 type = H_KEYDATA; 00149 CDB___ham_putitem(pagep, &argp->data, type); 00150 } else 00151 (void) CDB___ham_reputpair(pagep, file_dbp->pgsize, 00152 argp->ndx, &argp->key, &argp->data); 00153 00154 LSN(pagep) = DB_REDO(op) ? *lsnp : argp->pagelsn; 00155 if ((ret = CDB___ham_put_page(file_dbp, pagep, 1)) != 0) 00156 goto out; 00157 00158 } else if ((opcode == DELPAIR && cmp_p == 0 && DB_REDO(op)) 00159 || (opcode == PUTPAIR && cmp_n == 0 && DB_UNDO(op))) { 00160 /* Need to undo a put or redo a delete. */ 00161 CDB___ham_dpair(file_dbp, pagep, argp->ndx); 00162 LSN(pagep) = DB_REDO(op) ? *lsnp : argp->pagelsn; 00163 if ((ret = CDB___ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0) 00164 goto out; 00165 } else 00166 if ((ret = CDB___ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0) 00167 goto out; 00168 00169 /* Return the previous LSN. */ 00170 done: *lsnp = argp->prev_lsn; 00171 ret = 0; 00172 00173 out: if (getmeta) 00174 (void)CDB___ham_release_meta(dbc); 00175 REC_CLOSE; 00176 } 00177 00178 /* 00179 * CDB___ham_newpage_recover -- 00180 * This log message is used when we add/remove overflow pages. This 00181 * message takes care of the pointer chains, not the data on the pages. 00182 * 00183 * PUBLIC: int CDB___ham_newpage_recover 00184 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00185 */ 00186 int 00187 CDB___ham_newpage_recover(dbenv, dbtp, lsnp, op, info) 00188 DB_ENV *dbenv; 00189 DBT *dbtp; 00190 DB_LSN *lsnp; 00191 db_recops op; 00192 void *info; 00193 { 00194 __ham_newpage_args *argp; 00195 DB *file_dbp; 00196 DBC *dbc; 00197 DB_MPOOLFILE *mpf; 00198 PAGE *pagep; 00199 int cmp_n, cmp_p, change, getmeta, ret; 00200 00201 COMPQUIET(info, NULL); 00202 00203 getmeta = 0; 00204 REC_PRINT(CDB___ham_newpage_print); 00205 REC_INTRO(CDB___ham_newpage_read, 1); 00206 00207 if ((ret = CDB_memp_fget(mpf, &argp->new_pgno, 0, &pagep)) != 0) { 00208 if (DB_UNDO(op)) { 00209 /* 00210 * We are undoing and the page doesn't exist. That 00211 * is equivalent to having a pagelsn of 0, so we 00212 * would not have to undo anything. In this case, 00213 * don't bother creating a page. 00214 */ 00215 ret = 0; 00216 goto ppage; 00217 } else if ((ret = CDB_memp_fget(mpf, &argp->new_pgno, 00218 DB_MPOOL_CREATE, &pagep)) != 0) 00219 goto out; 00220 } 00221 00222 if ((ret = CDB___ham_get_meta(dbc)) != 0) 00223 goto out; 00224 getmeta = 1; 00225 00226 /* 00227 * There are potentially three pages we need to check: the one 00228 * that we created/deleted, the one before it and the one after 00229 * it. 00230 */ 00231 00232 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00233 cmp_p = CDB_log_compare(&LSN(pagep), &argp->pagelsn); 00234 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->pagelsn); 00235 change = 0; 00236 00237 if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == PUTOVFL) || 00238 (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DELOVFL)) { 00239 /* Redo a create new page or undo a delete new page. */ 00240 P_INIT(pagep, file_dbp->pgsize, argp->new_pgno, 00241 argp->prev_pgno, argp->next_pgno, 0, P_HASH, 0); 00242 change = 1; 00243 } else if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == DELOVFL) || 00244 (cmp_n == 0 && DB_UNDO(op) && argp->opcode == PUTOVFL)) { 00245 /* 00246 * Redo a delete or undo a create new page. All we 00247 * really need to do is change the LSN. 00248 */ 00249 change = 1; 00250 } 00251 00252 if (!change) { 00253 if ((ret = CDB___ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0) 00254 goto out; 00255 } else { 00256 LSN(pagep) = DB_REDO(op) ? *lsnp : argp->pagelsn; 00257 if ((ret = CDB___ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0) 00258 goto out; 00259 } 00260 00261 /* Now do the prev page. */ 00262 ppage: if (argp->prev_pgno != PGNO_INVALID) { 00263 if ((ret = CDB_memp_fget(mpf, &argp->prev_pgno, 0, &pagep)) != 0) { 00264 if (DB_UNDO(op)) { 00265 /* 00266 * We are undoing and the page doesn't exist. 00267 * That is equivalent to having a pagelsn of 0, 00268 * so we would not have to undo anything. In 00269 * this case, don't bother creating a page. 00270 */ 00271 ret = 0; 00272 goto npage; 00273 } else if ((ret = 00274 CDB_memp_fget(mpf, &argp->prev_pgno, 00275 DB_MPOOL_CREATE, &pagep)) != 0) 00276 goto out; 00277 } 00278 00279 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00280 cmp_p = CDB_log_compare(&LSN(pagep), &argp->prevlsn); 00281 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->prevlsn); 00282 change = 0; 00283 00284 if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == PUTOVFL) || 00285 (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DELOVFL)) { 00286 /* Redo a create new page or undo a delete new page. */ 00287 pagep->next_pgno = argp->new_pgno; 00288 change = 1; 00289 } else if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == DELOVFL) || 00290 (cmp_n == 0 && DB_UNDO(op) && argp->opcode == PUTOVFL)) { 00291 /* Redo a delete or undo a create new page. */ 00292 pagep->next_pgno = argp->next_pgno; 00293 change = 1; 00294 } 00295 00296 if (!change) { 00297 if ((ret = 00298 CDB___ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0) 00299 goto out; 00300 } else { 00301 LSN(pagep) = DB_REDO(op) ? *lsnp : argp->prevlsn; 00302 if ((ret = 00303 CDB___ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0) 00304 goto out; 00305 } 00306 } 00307 00308 /* Now time to do the next page */ 00309 npage: if (argp->next_pgno != PGNO_INVALID) { 00310 if ((ret = CDB_memp_fget(mpf, &argp->next_pgno, 0, &pagep)) != 0) { 00311 if (DB_UNDO(op)) { 00312 /* 00313 * We are undoing and the page doesn't exist. 00314 * That is equivalent to having a pagelsn of 0, 00315 * so we would not have to undo anything. In 00316 * this case, don't bother creating a page. 00317 */ 00318 goto done; 00319 } else if ((ret = 00320 CDB_memp_fget(mpf, &argp->next_pgno, 00321 DB_MPOOL_CREATE, &pagep)) != 0) 00322 goto out; 00323 } 00324 00325 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00326 cmp_p = CDB_log_compare(&LSN(pagep), &argp->nextlsn); 00327 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->nextlsn); 00328 change = 0; 00329 00330 if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == PUTOVFL) || 00331 (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DELOVFL)) { 00332 /* Redo a create new page or undo a delete new page. */ 00333 pagep->prev_pgno = argp->new_pgno; 00334 change = 1; 00335 } else if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == DELOVFL) || 00336 (cmp_n == 0 && DB_UNDO(op) && argp->opcode == PUTOVFL)) { 00337 /* Redo a delete or undo a create new page. */ 00338 pagep->prev_pgno = argp->prev_pgno; 00339 change = 1; 00340 } 00341 00342 if (!change) { 00343 if ((ret = 00344 CDB___ham_put_page(file_dbp, (PAGE *)pagep, 0)) != 0) 00345 goto out; 00346 } else { 00347 LSN(pagep) = DB_REDO(op) ? *lsnp : argp->nextlsn; 00348 if ((ret = 00349 CDB___ham_put_page(file_dbp, (PAGE *)pagep, 1)) != 0) 00350 goto out; 00351 } 00352 } 00353 done: *lsnp = argp->prev_lsn; 00354 ret = 0; 00355 00356 out: if (getmeta) 00357 (void)CDB___ham_release_meta(dbc); 00358 REC_CLOSE; 00359 } 00360 00361 /* 00362 * CDB___ham_replace_recover -- 00363 * This log message refers to partial puts that are local to a single 00364 * page. You can think of them as special cases of the more general 00365 * insdel log message. 00366 * 00367 * PUBLIC: int CDB___ham_replace_recover 00368 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00369 */ 00370 int 00371 CDB___ham_replace_recover(dbenv, dbtp, lsnp, op, info) 00372 DB_ENV *dbenv; 00373 DBT *dbtp; 00374 DB_LSN *lsnp; 00375 db_recops op; 00376 void *info; 00377 { 00378 __ham_replace_args *argp; 00379 DB *file_dbp; 00380 DBC *dbc; 00381 DB_MPOOLFILE *mpf; 00382 DBT dbt; 00383 PAGE *pagep; 00384 int32_t grow; 00385 int change, cmp_n, cmp_p, getmeta, ret; 00386 u_int8_t *hk; 00387 00388 COMPQUIET(info, NULL); 00389 00390 getmeta = 0; 00391 REC_PRINT(CDB___ham_replace_print); 00392 REC_INTRO(CDB___ham_replace_read, 1); 00393 00394 if ((ret = CDB_memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) { 00395 if (DB_UNDO(op)) { 00396 /* 00397 * We are undoing and the page doesn't exist. That 00398 * is equivalent to having a pagelsn of 0, so we 00399 * would not have to undo anything. In this case, 00400 * don't bother creating a page. 00401 */ 00402 goto done; 00403 } else if ((ret = CDB_memp_fget(mpf, &argp->pgno, 00404 DB_MPOOL_CREATE, &pagep)) != 0) 00405 goto out; 00406 } 00407 00408 if ((ret = CDB___ham_get_meta(dbc)) != 0) 00409 goto out; 00410 getmeta = 1; 00411 00412 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00413 cmp_p = CDB_log_compare(&LSN(pagep), &argp->pagelsn); 00414 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->pagelsn); 00415 00416 memset(&dbt, 0, sizeof(dbt)); 00417 if (cmp_p == 0 && DB_REDO(op)) { 00418 change = 1; 00419 /* Reapply the change as specified. */ 00420 dbt.data = argp->newitem.data; 00421 dbt.size = argp->newitem.size; 00422 grow = argp->newitem.size - argp->olditem.size; 00423 LSN(pagep) = *lsnp; 00424 } else if (cmp_n == 0 && DB_UNDO(op)) { 00425 change = 1; 00426 /* Undo the already applied change. */ 00427 dbt.data = argp->olditem.data; 00428 dbt.size = argp->olditem.size; 00429 grow = argp->olditem.size - argp->newitem.size; 00430 LSN(pagep) = argp->pagelsn; 00431 } else { 00432 change = 0; 00433 grow = 0; 00434 } 00435 00436 if (change) { 00437 CDB___ham_onpage_replace(pagep, 00438 file_dbp->pgsize, argp->ndx, argp->off, grow, &dbt); 00439 if (argp->makedup) { 00440 hk = P_ENTRY(pagep, argp->ndx); 00441 if (DB_REDO(op)) 00442 HPAGE_PTYPE(hk) = H_DUPLICATE; 00443 else 00444 HPAGE_PTYPE(hk) = H_KEYDATA; 00445 } 00446 } 00447 00448 if ((ret = CDB___ham_put_page(file_dbp, pagep, change)) != 0) 00449 goto out; 00450 00451 done: *lsnp = argp->prev_lsn; 00452 ret = 0; 00453 00454 out: if (getmeta) 00455 (void)CDB___ham_release_meta(dbc); 00456 REC_CLOSE; 00457 } 00458 00459 /* 00460 * CDB___ham_splitdata_recover -- 00461 * 00462 * PUBLIC: int CDB___ham_splitdata_recover 00463 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00464 */ 00465 int 00466 CDB___ham_splitdata_recover(dbenv, dbtp, lsnp, op, info) 00467 DB_ENV *dbenv; 00468 DBT *dbtp; 00469 DB_LSN *lsnp; 00470 db_recops op; 00471 void *info; 00472 { 00473 __ham_splitdata_args *argp; 00474 DB *file_dbp; 00475 DBC *dbc; 00476 DB_MPOOLFILE *mpf; 00477 PAGE *pagep; 00478 int change, cmp_n, cmp_p, getmeta, ret; 00479 00480 COMPQUIET(info, NULL); 00481 00482 getmeta = 0; 00483 REC_PRINT(CDB___ham_splitdata_print); 00484 REC_INTRO(CDB___ham_splitdata_read, 1); 00485 00486 if ((ret = CDB_memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) { 00487 if (DB_UNDO(op)) { 00488 /* 00489 * We are undoing and the page doesn't exist. That 00490 * is equivalent to having a pagelsn of 0, so we 00491 * would not have to undo anything. In this case, 00492 * don't bother creating a page. 00493 */ 00494 goto done; 00495 } else if ((ret = CDB_memp_fget(mpf, &argp->pgno, 00496 DB_MPOOL_CREATE, &pagep)) != 0) 00497 goto out; 00498 } 00499 00500 if ((ret = CDB___ham_get_meta(dbc)) != 0) 00501 goto out; 00502 getmeta = 1; 00503 00504 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00505 cmp_p = CDB_log_compare(&LSN(pagep), &argp->pagelsn); 00506 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->pagelsn); 00507 00508 /* 00509 * There are two types of log messages here, one for the old page 00510 * and one for the new pages created. The original image in the 00511 * SPLITOLD record is used for undo. The image in the SPLITNEW 00512 * is used for redo. We should never have a case where there is 00513 * a redo operation and the SPLITOLD record is on disk, but not 00514 * the SPLITNEW record. Therefore, we only have work to do when 00515 * redo NEW messages and undo OLD messages, but we have to update 00516 * LSNs in both cases. 00517 */ 00518 change = 0; 00519 if (cmp_p == 0 && DB_REDO(op)) { 00520 if (argp->opcode == SPLITNEW) 00521 /* Need to redo the split described. */ 00522 memcpy(pagep, argp->pageimage.data, 00523 argp->pageimage.size); 00524 LSN(pagep) = *lsnp; 00525 change = 1; 00526 } else if (cmp_n == 0 && DB_UNDO(op)) { 00527 if (argp->opcode == SPLITOLD) { 00528 /* Put back the old image. */ 00529 memcpy(pagep, argp->pageimage.data, 00530 argp->pageimage.size); 00531 } else 00532 P_INIT(pagep, file_dbp->pgsize, argp->pgno, 00533 PGNO_INVALID, PGNO_INVALID, 0, P_HASH, 0); 00534 LSN(pagep) = argp->pagelsn; 00535 change = 1; 00536 } 00537 if ((ret = CDB___ham_put_page(file_dbp, pagep, change)) != 0) 00538 goto out; 00539 00540 done: *lsnp = argp->prev_lsn; 00541 ret = 0; 00542 00543 out: if (getmeta) 00544 (void)CDB___ham_release_meta(dbc); 00545 REC_CLOSE; 00546 } 00547 00548 /* 00549 * CDB___ham_copypage_recover -- 00550 * Recovery function for copypage. 00551 * 00552 * PUBLIC: int CDB___ham_copypage_recover 00553 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00554 */ 00555 int 00556 CDB___ham_copypage_recover(dbenv, dbtp, lsnp, op, info) 00557 DB_ENV *dbenv; 00558 DBT *dbtp; 00559 DB_LSN *lsnp; 00560 db_recops op; 00561 void *info; 00562 { 00563 __ham_copypage_args *argp; 00564 DB *file_dbp; 00565 DBC *dbc; 00566 DB_MPOOLFILE *mpf; 00567 PAGE *pagep; 00568 int cmp_n, cmp_p, getmeta, modified, ret; 00569 00570 COMPQUIET(info, NULL); 00571 00572 getmeta = 0; 00573 REC_PRINT(CDB___ham_copypage_print); 00574 REC_INTRO(CDB___ham_copypage_read, 1); 00575 00576 if ((ret = CDB___ham_get_meta(dbc)) != 0) 00577 goto out; 00578 getmeta = 1; 00579 modified = 0; 00580 00581 /* This is the bucket page. */ 00582 if ((ret = CDB_memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) { 00583 if (DB_UNDO(op)) { 00584 /* 00585 * We are undoing and the page doesn't exist. That 00586 * is equivalent to having a pagelsn of 0, so we 00587 * would not have to undo anything. In this case, 00588 * don't bother creating a page. 00589 */ 00590 ret = 0; 00591 goto donext; 00592 } else if ((ret = CDB_memp_fget(mpf, &argp->pgno, 00593 DB_MPOOL_CREATE, &pagep)) != 0) 00594 goto out; 00595 } 00596 00597 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00598 cmp_p = CDB_log_compare(&LSN(pagep), &argp->pagelsn); 00599 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->pagelsn); 00600 00601 if (cmp_p == 0 && DB_REDO(op)) { 00602 /* Need to redo update described. */ 00603 memcpy(pagep, argp->page.data, argp->page.size); 00604 LSN(pagep) = *lsnp; 00605 modified = 1; 00606 } else if (cmp_n == 0 && DB_UNDO(op)) { 00607 /* Need to undo update described. */ 00608 P_INIT(pagep, file_dbp->pgsize, argp->pgno, PGNO_INVALID, 00609 argp->next_pgno, 0, P_HASH, 0); 00610 LSN(pagep) = argp->pagelsn; 00611 modified = 1; 00612 } 00613 if ((ret = CDB_memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) 00614 goto out; 00615 00616 donext: /* Now fix up the "next" page. */ 00617 if ((ret = CDB_memp_fget(mpf, &argp->next_pgno, 0, &pagep)) != 0) { 00618 if (DB_UNDO(op)) { 00619 /* 00620 * We are undoing and the page doesn't exist. That 00621 * is equivalent to having a pagelsn of 0, so we 00622 * would not have to undo anything. In this case, 00623 * don't bother creating a page. 00624 */ 00625 ret = 0; 00626 goto do_nn; 00627 } else if ((ret = CDB_memp_fget(mpf, &argp->next_pgno, 00628 DB_MPOOL_CREATE, &pagep)) != 0) 00629 goto out; 00630 } 00631 00632 modified = 0; 00633 /* For REDO just update the LSN. For UNDO copy page back. */ 00634 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00635 cmp_p = CDB_log_compare(&LSN(pagep), &argp->nextlsn); 00636 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->nextlsn); 00637 if (cmp_p == 0 && DB_REDO(op)) { 00638 LSN(pagep) = *lsnp; 00639 modified = 1; 00640 } else if (cmp_n == 0 && DB_UNDO(op)) { 00641 /* Need to undo update described. */ 00642 memcpy(pagep, argp->page.data, argp->page.size); 00643 modified = 1; 00644 } 00645 if ((ret = CDB_memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) 00646 goto out; 00647 00648 /* Now fix up the next's next page. */ 00649 do_nn: if (argp->nnext_pgno == PGNO_INVALID) 00650 goto done; 00651 00652 if ((ret = CDB_memp_fget(mpf, &argp->nnext_pgno, 0, &pagep)) != 0) { 00653 if (DB_UNDO(op)) { 00654 /* 00655 * We are undoing and the page doesn't exist. That 00656 * is equivalent to having a pagelsn of 0, so we 00657 * would not have to undo anything. In this case, 00658 * don't bother creating a page. 00659 */ 00660 goto done; 00661 } else if ((ret = CDB_memp_fget(mpf, &argp->nnext_pgno, 00662 DB_MPOOL_CREATE, &pagep)) != 0) 00663 goto out; 00664 } 00665 00666 modified = 0; 00667 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00668 cmp_p = CDB_log_compare(&LSN(pagep), &argp->nnextlsn); 00669 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->nnextlsn); 00670 00671 if (cmp_p == 0 && DB_REDO(op)) { 00672 /* Need to redo update described. */ 00673 PREV_PGNO(pagep) = argp->pgno; 00674 LSN(pagep) = *lsnp; 00675 modified = 1; 00676 } else if (cmp_n == 0 && DB_UNDO(op)) { 00677 /* Need to undo update described. */ 00678 PREV_PGNO(pagep) = argp->next_pgno; 00679 LSN(pagep) = argp->nnextlsn; 00680 modified = 1; 00681 } 00682 if ((ret = CDB_memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) 00683 goto out; 00684 00685 done: *lsnp = argp->prev_lsn; 00686 ret = 0; 00687 00688 out: if (getmeta) 00689 (void)CDB___ham_release_meta(dbc); 00690 REC_CLOSE; 00691 } 00692 00693 /* 00694 * CDB___ham_metagroup_recover -- 00695 * Recovery function for metagroup. 00696 * 00697 * PUBLIC: int CDB___ham_metagroup_recover 00698 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00699 */ 00700 int 00701 CDB___ham_metagroup_recover(dbenv, dbtp, lsnp, op, info) 00702 DB_ENV *dbenv; 00703 DBT *dbtp; 00704 DB_LSN *lsnp; 00705 db_recops op; 00706 void *info; 00707 { 00708 __ham_metagroup_args *argp; 00709 HASH_CURSOR *hcp; 00710 DB *file_dbp; 00711 DBC *dbc; 00712 DB_MPOOLFILE *mpf; 00713 PAGE *pagep; 00714 db_pgno_t last_pgno; 00715 int cmp_n, cmp_p, groupgrow, modified, ret; 00716 00717 COMPQUIET(info, NULL); 00718 REC_PRINT(CDB___ham_metagroup_print); 00719 REC_INTRO(CDB___ham_metagroup_read, 1); 00720 00721 /* 00722 * This logs the virtual create of pages pgno to pgno + bucket 00723 * Since the mpool page-allocation is not really able to be 00724 * transaction protected, we can never undo it. Even in an abort, 00725 * we have to allocate these pages to the hash table. 00726 * The log record contains: 00727 * bucket: new bucket being allocated. 00728 * pgno: page number of the new bucket. 00729 * if bucket is a power of 2, then we allocated a whole batch of 00730 * pages; if it's not, then we simply allocated one new page. 00731 */ 00732 groupgrow = 00733 (u_int32_t)(1 << CDB___db_log2(argp->bucket + 1)) == argp->bucket + 1; 00734 00735 last_pgno = argp->pgno; 00736 if (groupgrow) 00737 /* Read the last page. */ 00738 last_pgno += argp->bucket; 00739 00740 if ((ret = CDB_memp_fget(mpf, &last_pgno, DB_MPOOL_CREATE, &pagep)) != 0) 00741 goto out; 00742 00743 modified = 0; 00744 cmp_n = CDB_log_compare(lsnp, &LSN(pagep)); 00745 cmp_p = CDB_log_compare(&LSN(pagep), &argp->pagelsn); 00746 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->pagelsn); 00747 00748 if ((cmp_p == 0 && DB_REDO(op)) || (cmp_n == 0 && DB_UNDO(op))) { 00749 /* 00750 * We need to make sure that we redo the allocation of the 00751 * pages. 00752 */ 00753 if (DB_REDO(op)) 00754 pagep->lsn = *lsnp; 00755 else 00756 pagep->lsn = argp->pagelsn; 00757 modified = 1; 00758 } 00759 if ((ret = CDB_memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) 00760 goto out; 00761 00762 /* Now we have to update the meta-data page. */ 00763 hcp = (HASH_CURSOR *)dbc->internal; 00764 if ((ret = CDB___ham_get_meta(dbc)) != 0) 00765 goto out; 00766 cmp_n = CDB_log_compare(lsnp, &hcp->hdr->dbmeta.lsn); 00767 cmp_p = CDB_log_compare(&hcp->hdr->dbmeta.lsn, &argp->metalsn); 00768 CHECK_LSN(op, cmp_p, &hcp->hdr->dbmeta.lsn, &argp->metalsn); 00769 if ((cmp_p == 0 && DB_REDO(op)) || (cmp_n == 0 && DB_UNDO(op))) { 00770 if (DB_REDO(op)) { 00771 /* Redo the actual updating of bucket counts. */ 00772 ++hcp->hdr->max_bucket; 00773 if (groupgrow) { 00774 hcp->hdr->low_mask = hcp->hdr->high_mask; 00775 hcp->hdr->high_mask = 00776 (argp->bucket + 1) | hcp->hdr->low_mask; 00777 } 00778 hcp->hdr->dbmeta.lsn = *lsnp; 00779 } else { 00780 /* Undo the actual updating of bucket counts. */ 00781 --hcp->hdr->max_bucket; 00782 if (groupgrow) { 00783 hcp->hdr->high_mask = hcp->hdr->low_mask; 00784 hcp->hdr->low_mask = hcp->hdr->high_mask >> 1; 00785 } 00786 hcp->hdr->dbmeta.lsn = argp->metalsn; 00787 } 00788 if (groupgrow && 00789 hcp->hdr->spares[CDB___db_log2(argp->bucket + 1) + 1] == 00790 PGNO_INVALID) 00791 hcp->hdr->spares[CDB___db_log2(argp->bucket + 1) + 1] = 00792 argp->pgno - argp->bucket - 1; 00793 F_SET(hcp, H_DIRTY); 00794 } 00795 if ((ret = CDB___ham_release_meta(dbc)) != 0) 00796 goto out; 00797 00798 done: *lsnp = argp->prev_lsn; 00799 ret = 0; 00800 00801 out: REC_CLOSE; 00802 } 00803 00804 /* 00805 * CDB___ham_groupalloc_recover -- 00806 * Recover the batch creation of a set of pages for a new database. 00807 * 00808 * PUBLIC: int CDB___ham_groupalloc_recover 00809 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00810 */ 00811 int 00812 CDB___ham_groupalloc_recover(dbenv, dbtp, lsnp, op, info) 00813 DB_ENV *dbenv; 00814 DBT *dbtp; 00815 DB_LSN *lsnp; 00816 db_recops op; 00817 void *info; 00818 { 00819 __ham_groupalloc_args *argp; 00820 DBMETA *mmeta; 00821 DB_MPOOLFILE *mpf; 00822 DB *file_dbp; 00823 DBC *dbc; 00824 db_pgno_t pgno; 00825 int cmp_n, cmp_p, modified, ret; 00826 00827 modified = 0; 00828 COMPQUIET(info, NULL); 00829 REC_PRINT(CDB___ham_groupalloc_print); 00830 REC_INTRO(CDB___ham_groupalloc_read, 0); 00831 00832 pgno = PGNO_BASE_MD; 00833 if ((ret = CDB_memp_fget(mpf, &pgno, 0, &mmeta)) != 0) { 00834 if (DB_REDO(op)) { 00835 /* Page should have existed. */ 00836 (void)CDB___db_pgerr(file_dbp, pgno); 00837 goto out; 00838 } else { 00839 ret = 0; 00840 goto done; 00841 } 00842 } 00843 00844 cmp_n = CDB_log_compare(lsnp, &mmeta->alloc_lsn); 00845 cmp_p = CDB_log_compare(&mmeta->alloc_lsn, &argp->alloc_lsn); 00846 CHECK_LSN(op, cmp_p, &mmeta->alloc_lsn, &argp->alloc_lsn); 00847 00848 /* 00849 * Basically, we used mpool to allocate a chunk of pages. 00850 * We need to either add those to a free list (in the undo 00851 * case) or initialize them (in the redo case). 00852 * 00853 * If we are redoing and this is a hash subdatabase, it's possible 00854 * that the pages were never allocated, so we'd better check for 00855 * that and handle it here. 00856 */ 00857 00858 if (DB_REDO(op)) { 00859 if ((ret = __ham_alloc_pages(file_dbp, argp)) != 0) 00860 goto out1; 00861 if (cmp_p == 0) { 00862 mmeta->alloc_lsn = *lsnp; 00863 if (CDB_log_compare(&LSN(mmeta), &argp->meta_lsn) == 0) 00864 LSN(mmeta) = *lsnp; 00865 modified = 1; 00866 } 00867 } 00868 00869 /* 00870 * We must determine if this is the first allocation to be 00871 * undone. If the LSN is not right, then it must be the first. 00872 * If the LSN is correct, then look to see if the pages allocated 00873 * by this log record are at the head of the list. If so then we 00874 * have previously undone this allocation and we are starting over. 00875 * If not, we have not undone this allocation and some previous 00876 * undo of an allocation has put us in the right state. We 00877 * just undo the allocation by putting the pages on the free list. 00878 */ 00879 else if (DB_UNDO(op)) { 00880 if (cmp_n != 0 || 00881 (mmeta->free >= argp->start_pgno && 00882 mmeta->free < argp->start_pgno + argp->num)) 00883 mmeta->free = argp->free; 00884 if ((ret = __ham_free_pages(file_dbp, mmeta, argp)) != 0) 00885 goto out1; 00886 mmeta->alloc_lsn = argp->alloc_lsn; 00887 if (CDB_log_compare(lsnp, &LSN(mmeta)) == 0) 00888 LSN(mmeta) = argp->meta_lsn; 00889 modified = 1; 00890 } 00891 00892 out1: if ((ret = CDB_memp_fput(mpf, mmeta, modified ? DB_MPOOL_DIRTY : 0)) != 0) 00893 goto out; 00894 00895 done: if (ret == 0) 00896 *lsnp = argp->prev_lsn; 00897 00898 out: REC_CLOSE; 00899 } 00900 00901 /* 00902 * __ham_free_pages -- 00903 * 00904 * Called during abort/undo of a file create. We create new pages in the file 00905 * using the MPOOL_NEW_GROUP flag. We then log the meta-data page with a 00906 * __crdel_metasub message. If we fail we need to take those newly allocated 00907 * pages and put them on a free list. Normally this would happen in the 00908 * recovery for CDB___db_new, but that doesn't get called in this case. 00909 * We do not look at LSNs here because we are forcing the pages to their 00910 * deallocated state. If they were reallocated after this, they 00911 * will get rolled forward later. 00912 */ 00913 static int 00914 __ham_free_pages(dbp, mmeta, argp) 00915 DB *dbp; 00916 DBMETA *mmeta; 00917 __ham_groupalloc_args *argp; 00918 { 00919 DB_MPOOLFILE *mpf; 00920 PAGE *pagep; 00921 u_int32_t i; 00922 db_pgno_t last_free, pgno; 00923 int ret; 00924 00925 mpf = dbp->mpf; 00926 last_free = mmeta->free; 00927 00928 for (i = 0; i < argp->num; i++, last_free = pgno) { 00929 pgno = argp->start_pgno + i; 00930 if ((ret = 00931 CDB_memp_fget(mpf, &pgno, DB_MPOOL_CREATE, &pagep)) != 0) { 00932 (void)CDB___db_pgerr(dbp, pgno); 00933 return (ret); 00934 } 00935 00936 /* Fix up the allocated page. */ 00937 P_INIT(pagep, 00938 dbp->pgsize, pgno, PGNO_INVALID, last_free, 0, P_INVALID, 0); 00939 ZERO_LSN(pagep->lsn); 00940 00941 if ((ret = CDB_memp_fput(mpf, pagep, DB_MPOOL_DIRTY)) != 0) 00942 return (ret); 00943 } 00944 00945 mmeta->free = last_free; 00946 return (0); 00947 } 00948 00949 /* 00950 * __ham_alloc_pages -- 00951 * 00952 * Called during redo of a file create. We create new pages in the file 00953 * using the MPOOL_NEW_GROUP flag. We then log the meta-data page with a 00954 * __crdel_metasub message. If we manage to crash without the newly written 00955 * pages getting to disk (I'm not sure this can happen anywhere except our 00956 * test suite?!), then we need to go through a recreate the final pages. 00957 * Hash normally has holes in its files and handles them appropriately. 00958 */ 00959 static int 00960 __ham_alloc_pages(dbp, argp) 00961 DB *dbp; 00962 __ham_groupalloc_args *argp; 00963 { 00964 DB_MPOOLFILE *mpf; 00965 PAGE *pagep; 00966 db_pgno_t pgno; 00967 int ret; 00968 00969 mpf = dbp->mpf; 00970 00971 /* Read the last page of the allocation. */ 00972 pgno = argp->start_pgno + argp->num - 1; 00973 00974 /* If the page exists, and it has been initialized, then we're done. */ 00975 if ((ret = CDB_memp_fget(mpf, &pgno, 0, &pagep)) == 0) { 00976 if ((TYPE(pagep) == P_INVALID) && IS_ZERO_LSN(pagep->lsn)) 00977 goto reinit_page; 00978 if ((ret = CDB_memp_fput(mpf, pagep, 0)) != 0) 00979 return (ret); 00980 return (0); 00981 } 00982 00983 /* 00984 * Had to create the page. On some systems (read "Windows"), 00985 * you can find random garbage on pages to which you haven't 00986 * yet written. So, we have an os layer that will do the 00987 * right thing for group allocations. We call that directly 00988 * to make sure all the pages are allocated and then continue 00989 * merrily on our way with normal recovery. 00990 */ 00991 if ((ret = CDB___os_fpinit(dbp->dbenv, &mpf->fh, 00992 argp->start_pgno, argp->num, dbp->pgsize)) != 0) 00993 return (ret); 00994 00995 if ((ret = CDB_memp_fget(mpf, &pgno, DB_MPOOL_CREATE, &pagep)) != 0) { 00996 (void)CDB___db_pgerr(dbp, pgno); 00997 return (ret); 00998 } 00999 01000 reinit_page: 01001 /* Initialize the newly allocated page. */ 01002 P_INIT(pagep, 01003 dbp->pgsize, pgno, PGNO_INVALID, PGNO_INVALID, 0, P_HASH, 0); 01004 ZERO_LSN(pagep->lsn); 01005 01006 if ((ret = CDB_memp_fput(mpf, pagep, DB_MPOOL_DIRTY)) != 0) 01007 return (ret); 01008 01009 return (0); 01010 }