Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

db_remove.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 2001-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: db_remove.c,v 12.16 2005/10/27 01:25:53 mjc Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #include <string.h>
00016 #endif
00017 
00018 #include "db_int.h"
00019 #include "dbinc/db_page.h"
00020 #include "dbinc/fop.h"
00021 #include "dbinc/btree.h"
00022 #include "dbinc/hash.h"
00023 #include "dbinc/db_shash.h"
00024 #include "dbinc/lock.h"
00025 #include "dbinc/mp.h"
00026 #include "dbinc/txn.h"
00027 
00028 static int __db_dbtxn_remove __P((DB *, DB_TXN *, const char *, const char *));
00029 static int __db_subdb_remove __P((DB *, DB_TXN *, const char *, const char *));
00030 
00031 /*
00032  * __env_dbremove_pp
00033  *      DB_ENV->dbremove pre/post processing.
00034  *
00035  * PUBLIC: int __env_dbremove_pp __P((DB_ENV *,
00036  * PUBLIC:     DB_TXN *, const char *, const char *, u_int32_t));
00037  */
00038 int
00039 __env_dbremove_pp(dbenv, txn, name, subdb, flags)
00040         DB_ENV *dbenv;
00041         DB_TXN *txn;
00042         const char *name, *subdb;
00043         u_int32_t flags;
00044 {
00045         DB *dbp;
00046         DB_THREAD_INFO *ip;
00047         int handle_check, ret, t_ret, txn_local;
00048 
00049         dbp = NULL;
00050         txn_local = 0;
00051 
00052         PANIC_CHECK(dbenv);
00053         ENV_ILLEGAL_BEFORE_OPEN(dbenv, "DB_ENV->dbremove");
00054 
00055         /*
00056          * The actual argument checking is simple, do it inline, outside of
00057          * the replication block.
00058          */
00059         if ((ret = __db_fchk(dbenv, "DB->remove", flags, DB_AUTO_COMMIT)) != 0)
00060                 return (ret);
00061 
00062         ENV_ENTER(dbenv, ip);
00063 
00064         /* Check for replication block. */
00065         handle_check = IS_ENV_REPLICATED(dbenv);
00066         if (handle_check && (ret = __env_rep_enter(dbenv, 1)) != 0) {
00067                 handle_check = 0;
00068                 goto err;
00069         }
00070 
00071         /*
00072          * Create local transaction as necessary, check for consistent
00073          * transaction usage.
00074          */
00075         if (IS_ENV_AUTO_COMMIT(dbenv, txn, flags)) {
00076                 if ((ret = __db_txn_auto_init(dbenv, &txn)) != 0)
00077                         goto err;
00078                 txn_local = 1;
00079         } else
00080                 if (txn != NULL && !TXN_ON(dbenv)) {
00081                         ret = __db_not_txn_env(dbenv);
00082                         goto err;
00083                 }
00084         LF_CLR(DB_AUTO_COMMIT);
00085 
00086         if ((ret = db_create(&dbp, dbenv, 0)) != 0)
00087                 goto err;
00088 
00089         ret = __db_remove_int(dbp, txn, name, subdb, flags);
00090 
00091         if (txn_local) {
00092                 /*
00093                  * We created the DBP here and when we commit/abort, we'll
00094                  * release all the transactional locks, including the handle
00095                  * lock; mark the handle cleared explicitly.
00096                  */
00097                 LOCK_INIT(dbp->handle_lock);
00098                 dbp->lid = DB_LOCK_INVALIDID;
00099         } else if (txn != NULL) {
00100                 /*
00101                  * We created this handle locally so we need to close it
00102                  * and clean it up.  Unfortunately, it's holding transactional
00103                  * locks that need to persist until the end of transaction.
00104                  * If we invalidate the locker id (dbp->lid), then the close
00105                  * won't free these locks prematurely.
00106                  */
00107                  dbp->lid = DB_LOCK_INVALIDID;
00108         }
00109 
00110 err:    if (txn_local && (t_ret =
00111             __db_txn_auto_resolve(dbenv, txn, 0, ret)) != 0 && ret == 0)
00112                 ret = t_ret;
00113 
00114         /*
00115          * We never opened this dbp for real, so don't include a transaction
00116          * handle, and use NOSYNC to avoid calling into mpool.
00117          *
00118          * !!!
00119          * Note we're reversing the order of operations: we started the txn and
00120          * then opened the DB handle; we're resolving the txn and then closing
00121          * closing the DB handle -- it's safer.
00122          */
00123         if (dbp != NULL &&
00124             (t_ret = __db_close(dbp, NULL, DB_NOSYNC)) != 0 && ret == 0)
00125                 ret = t_ret;
00126 
00127         if (handle_check && (t_ret = __env_db_rep_exit(dbenv)) != 0 && ret == 0)
00128                 ret = t_ret;
00129 
00130         ENV_LEAVE(dbenv, ip);
00131         return (ret);
00132 }
00133 
00134 /*
00135  * __db_remove_pp
00136  *      DB->remove pre/post processing.
00137  *
00138  * PUBLIC: int __db_remove_pp
00139  * PUBLIC:     __P((DB *, const char *, const char *, u_int32_t));
00140  */
00141 int
00142 __db_remove_pp(dbp, name, subdb, flags)
00143         DB *dbp;
00144         const char *name, *subdb;
00145         u_int32_t flags;
00146 {
00147         DB_ENV *dbenv;
00148         DB_THREAD_INFO *ip;
00149         int handle_check, ret, t_ret;
00150 
00151         dbenv = dbp->dbenv;
00152 
00153         PANIC_CHECK(dbenv);
00154 
00155         /*
00156          * Validate arguments, continuing to destroy the handle on failure.
00157          *
00158          * Cannot use DB_ILLEGAL_AFTER_OPEN directly because it returns.
00159          *
00160          * !!!
00161          * We have a serious problem if we're here with a handle used to open
00162          * a database -- we'll destroy the handle, and the application won't
00163          * ever be able to close the database.
00164          */
00165         if (F_ISSET(dbp, DB_AM_OPEN_CALLED)) {
00166                 ret = __db_mi_open(dbenv, "DB->remove", 1);
00167                 return (ret);
00168         }
00169 
00170         /* Validate arguments. */
00171         if ((ret = __db_fchk(dbenv, "DB->remove", flags, 0)) != 0)
00172                 return (ret);
00173 
00174         /* Check for consistent transaction usage. */
00175         if ((ret = __db_check_txn(dbp, NULL, DB_LOCK_INVALIDID, 0)) != 0)
00176                 return (ret);
00177 
00178         ENV_ENTER(dbenv, ip);
00179 
00180         handle_check = IS_ENV_REPLICATED(dbenv);
00181         if (handle_check && (ret = __db_rep_enter(dbp, 1, 1, 0)) != 0) {
00182                 handle_check = 0;
00183                 goto err;
00184         }
00185 
00186         /* Remove the file. */
00187         ret = __db_remove(dbp, NULL, name, subdb, flags);
00188 
00189         if (handle_check && (t_ret = __env_db_rep_exit(dbenv)) != 0 && ret == 0)
00190                 ret = t_ret;
00191 
00192 err:    ENV_LEAVE(dbenv, ip);
00193         return (ret);
00194 }
00195 
00196 /*
00197  * __db_remove
00198  *      DB->remove method.
00199  *
00200  * PUBLIC: int __db_remove
00201  * PUBLIC:     __P((DB *, DB_TXN *, const char *, const char *, u_int32_t));
00202  */
00203 int
00204 __db_remove(dbp, txn, name, subdb, flags)
00205         DB *dbp;
00206         DB_TXN *txn;
00207         const char *name, *subdb;
00208         u_int32_t flags;
00209 {
00210         int ret, t_ret;
00211 
00212         ret = __db_remove_int(dbp, txn, name, subdb, flags);
00213 
00214         if ((t_ret = __db_close(dbp, txn, DB_NOSYNC)) != 0 && ret == 0)
00215                 ret = t_ret;
00216 
00217         return (ret);
00218 }
00219 
00220 /*
00221  * __db_remove_int
00222  *      Worker function for the DB->remove method.
00223  *
00224  * PUBLIC: int __db_remove_int __P((DB *,
00225  * PUBLIC:    DB_TXN *, const char *, const char *, u_int32_t));
00226  */
00227 int
00228 __db_remove_int(dbp, txn, name, subdb, flags)
00229         DB *dbp;
00230         DB_TXN *txn;
00231         const char *name, *subdb;
00232         u_int32_t flags;
00233 {
00234         DB_ENV *dbenv;
00235         int ret;
00236         char *real_name, *tmpname;
00237 
00238         dbenv = dbp->dbenv;
00239         real_name = tmpname = NULL;
00240 
00241         if (name == NULL && subdb == NULL) {
00242                 __db_err(dbenv, "Remove on temporary files invalid");
00243                 ret = EINVAL;
00244                 goto err;
00245         }
00246 
00247         if (name == NULL) {
00248                 MAKE_INMEM(dbp);
00249                 real_name = (char *)subdb;
00250         } else if (subdb != NULL) {
00251                 ret = __db_subdb_remove(dbp, txn, name, subdb);
00252                 goto err;
00253         }
00254 
00255         /* Handle transactional file removes separately. */
00256         if (txn != NULL) {
00257                 ret = __db_dbtxn_remove(dbp, txn, name, subdb);
00258                 goto err;
00259         }
00260 
00261         /*
00262          * The remaining case is a non-transactional file remove.
00263          *
00264          * Find the real name of the file.
00265          */
00266         if (!F_ISSET(dbp, DB_AM_INMEM) && (ret =
00267             __db_appname(dbenv, DB_APP_DATA, name, 0, NULL, &real_name)) != 0)
00268                 goto err;
00269 
00270         /*
00271          * If this is a file and force is set, remove the temporary file, which
00272          * may have been left around.  Ignore errors because the temporary file
00273          * might not exist.
00274          */
00275         if (!F_ISSET(dbp, DB_AM_INMEM) && LF_ISSET(DB_FORCE) &&
00276             (ret = __db_backup_name(dbenv, real_name, NULL, &tmpname)) == 0)
00277                 (void)__os_unlink(dbenv, tmpname);
00278 
00279         if ((ret = __fop_remove_setup(dbp, NULL, real_name, 0)) != 0)
00280                 goto err;
00281 
00282         if (dbp->db_am_remove != NULL &&
00283             (ret = dbp->db_am_remove(dbp, NULL, name, subdb)) != 0)
00284                 goto err;
00285 
00286         ret = F_ISSET(dbp, DB_AM_INMEM) ?
00287             __db_inmem_remove(dbp, NULL, real_name) :
00288             __fop_remove(dbenv, NULL, dbp->fileid, name, DB_APP_DATA,
00289             F_ISSET(dbp, DB_AM_NOT_DURABLE) ? DB_LOG_NOT_DURABLE : 0);
00290 
00291 err:    if (!F_ISSET(dbp, DB_AM_INMEM) && real_name != NULL)
00292                 __os_free(dbenv, real_name);
00293         if (tmpname != NULL)
00294                 __os_free(dbenv, tmpname);
00295 
00296         return (ret);
00297 }
00298 
00299 /*
00300  * __db_inmem_remove --
00301  *      Removal of a named in-memory database.
00302  * PUBLIC: int __db_inmem_remove __P((DB *, DB_TXN *, const char *));
00303  */
00304 int
00305 __db_inmem_remove(dbp, txn, name)
00306         DB *dbp;
00307         DB_TXN *txn;
00308         const char *name;
00309 {
00310         DB_ENV *dbenv;
00311         DB_LSN lsn;
00312         DBT fid_dbt, name_dbt;
00313         u_int32_t locker;
00314         int ret;
00315 
00316         dbenv = dbp->dbenv;
00317         locker = DB_LOCK_INVALIDID;
00318 
00319         DB_ASSERT(name != NULL);
00320 
00321         /* This had better exist if we are trying to do a remove. */
00322         (void)__memp_set_flags(dbp->mpf, DB_MPOOL_NOFILE, 1);
00323         if ((ret = __memp_fopen(dbp->mpf, NULL, name, 0, 0, 0)) != 0)
00324                 return (ret);
00325         if ((ret = __memp_get_fileid(dbp->mpf, dbp->fileid)) != 0)
00326                 goto err;
00327         dbp->preserve_fid = 1;
00328 
00329         if (LOCKING_ON(dbenv)) {
00330                 if (dbp->lid == DB_LOCK_INVALIDID &&
00331                     (ret = __lock_id(dbenv, &dbp->lid, NULL)) != 0)
00332                         goto err;
00333                 locker = txn == NULL ? dbp->lid : txn->txnid;
00334         }
00335 
00336         /*
00337          * In a transactional environment, we'll play the same game
00338          * that we play for databases in the file system -- create a
00339          * temporary database and put it in with the current name
00340          * and then rename this one to another name.  We'll then use
00341          * a commit-time event to remove the entry.
00342          */
00343 
00344         if ((ret = __fop_lock_handle(dbenv,
00345             dbp, locker, DB_LOCK_WRITE, NULL, 0)) != 0)
00346                 goto err;
00347 
00348         if (LOGGING_ON(dbenv)) {
00349                 memset(&fid_dbt, 0, sizeof(fid_dbt));
00350                 fid_dbt.data = dbp->fileid;
00351                 fid_dbt.size = DB_FILE_ID_LEN;
00352                 memset(&name_dbt, 0, sizeof(name_dbt));
00353                 name_dbt.data = (void *)name;
00354                 name_dbt.size = (u_int32_t)strlen(name) + 1;
00355 
00356                 if (txn != NULL && (ret =
00357                     __txn_remevent(dbenv, txn, name, dbp->fileid, 1)) != 0)
00358                         goto err;
00359 
00360                 if ((ret = __crdel_inmem_remove_log(dbenv,
00361                     txn, &lsn, 0, &name_dbt, &fid_dbt)) != 0)
00362                         goto err;
00363         }
00364 
00365         if (txn == NULL)
00366                 ret = __memp_nameop(dbenv, dbp->fileid, NULL, name, NULL, 1);
00367 
00368 err:    return (ret);
00369 }
00370 
00371 /*
00372  * __db_subdb_remove --
00373  *      Remove a subdatabase.
00374  */
00375 static int
00376 __db_subdb_remove(dbp, txn, name, subdb)
00377         DB *dbp;
00378         DB_TXN *txn;
00379         const char *name, *subdb;
00380 {
00381         DB *mdbp, *sdbp;
00382         int ret, t_ret;
00383 
00384         mdbp = sdbp = NULL;
00385 
00386         /* Open the subdatabase. */
00387         if ((ret = db_create(&sdbp, dbp->dbenv, 0)) != 0)
00388                 goto err;
00389         if ((ret = __db_open(sdbp,
00390             txn, name, subdb, DB_UNKNOWN, DB_WRITEOPEN, 0, PGNO_BASE_MD)) != 0)
00391                 goto err;
00392 
00393         DB_TEST_RECOVERY(sdbp, DB_TEST_PREDESTROY, ret, name);
00394 
00395         /* Free up the pages in the subdatabase. */
00396         switch (sdbp->type) {
00397                 case DB_BTREE:
00398                 case DB_RECNO:
00399                         if ((ret = __bam_reclaim(sdbp, txn)) != 0)
00400                                 goto err;
00401                         break;
00402                 case DB_HASH:
00403                         if ((ret = __ham_reclaim(sdbp, txn)) != 0)
00404                                 goto err;
00405                         break;
00406                 case DB_QUEUE:
00407                 case DB_UNKNOWN:
00408                 default:
00409                         ret = __db_unknown_type(
00410                             sdbp->dbenv, "__db_subdb_remove", sdbp->type);
00411                         goto err;
00412         }
00413 
00414         /*
00415          * Remove the entry from the main database and free the subdatabase
00416          * metadata page.
00417          */
00418         if ((ret = __db_master_open(sdbp, txn, name, 0, 0, &mdbp)) != 0)
00419                 goto err;
00420 
00421         if ((ret = __db_master_update(
00422             mdbp, sdbp, txn, subdb, sdbp->type, MU_REMOVE, NULL, 0)) != 0)
00423                 goto err;
00424 
00425         DB_TEST_RECOVERY(sdbp, DB_TEST_POSTDESTROY, ret, name);
00426 
00427 DB_TEST_RECOVERY_LABEL
00428 err:
00429         /* Close the main and subdatabases. */
00430         if ((t_ret = __db_close(sdbp, txn, 0)) != 0 && ret == 0)
00431                 ret = t_ret;
00432 
00433         if (mdbp != NULL &&
00434             (t_ret = __db_close(mdbp, txn, DB_NOSYNC)) != 0 && ret == 0)
00435                 ret = t_ret;
00436 
00437         return (ret);
00438 }
00439 
00440 static int
00441 __db_dbtxn_remove(dbp, txn, name, subdb)
00442         DB *dbp;
00443         DB_TXN *txn;
00444         const char *name, *subdb;
00445 {
00446         DB_ENV *dbenv;
00447         int ret;
00448         char *tmpname;
00449 
00450         dbenv = dbp->dbenv;
00451         tmpname = NULL;
00452 
00453         /*
00454          * This is a transactional remove, so we have to keep the name
00455          * of the file locked until the transaction commits.  As a result,
00456          * we implement remove by renaming the file to some other name
00457          * (which creates a dummy named file as a placeholder for the
00458          * file being rename/dremoved) and then deleting that file as
00459          * a delayed remove at commit.
00460          */
00461         if ((ret = __db_backup_name(dbenv,
00462             F_ISSET(dbp, DB_AM_INMEM) ? subdb : name, txn, &tmpname)) != 0)
00463                 return (ret);
00464 
00465         DB_TEST_RECOVERY(dbp, DB_TEST_PREDESTROY, ret, name);
00466 
00467         if ((ret = __db_rename_int(dbp, txn, name, subdb, tmpname)) != 0)
00468                 goto err;
00469 
00470         /*
00471          * The internal removes will also translate into delayed removes.
00472          */
00473         if (dbp->db_am_remove != NULL &&
00474             (ret = dbp->db_am_remove(dbp, txn, tmpname, NULL)) != 0)
00475                 goto err;
00476 
00477         ret = F_ISSET(dbp, DB_AM_INMEM) ?
00478              __db_inmem_remove(dbp, txn, tmpname) :
00479             __fop_remove(dbenv, txn, dbp->fileid, tmpname, DB_APP_DATA,
00480             F_ISSET(dbp, DB_AM_NOT_DURABLE) ? DB_LOG_NOT_DURABLE : 0);
00481 
00482         DB_TEST_RECOVERY(dbp, DB_TEST_POSTDESTROY, ret, name);
00483 
00484 err:
00485 DB_TEST_RECOVERY_LABEL
00486         if (tmpname != NULL)
00487                 __os_free(dbenv, tmpname);
00488 
00489         return (ret);
00490 }

Generated on Sun Dec 25 12:14:21 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2