hash_rec.c

Go to the documentation of this file.
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 }

Generated on Sun Jun 8 10:56:37 2008 for GNUmifluz by  doxygen 1.5.5