db_rename.c 8.84 KB
Newer Older
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
1 2 3
/*-
 * See the file LICENSE for redistribution information.
 *
jimw@mysql.com's avatar
jimw@mysql.com committed
4
 * Copyright (c) 2001-2005
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
5
 *	Sleepycat Software.  All rights reserved.
jimw@mysql.com's avatar
jimw@mysql.com committed
6
 *
jimw@mysql.com's avatar
jimw@mysql.com committed
7
 * $Id: db_rename.c,v 12.11 2005/10/07 20:21:22 ubell Exp $
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 */

#include "db_config.h"

#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <string.h>
#endif

#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/db_shash.h"
#include "dbinc/db_am.h"
#include "dbinc/fop.h"
#include "dbinc/lock.h"
#include "dbinc/log.h"
jimw@mysql.com's avatar
jimw@mysql.com committed
24
#include "dbinc/mp.h"
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
25

jimw@mysql.com's avatar
jimw@mysql.com committed
26 27
static int __db_subdb_rename __P((DB *,
	       DB_TXN *, const char *, const char *, const char *));
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
28 29

/*
jimw@mysql.com's avatar
jimw@mysql.com committed
30
 * __env_dbrename_pp
jimw@mysql.com's avatar
jimw@mysql.com committed
31
 *	DB_ENV->dbrename pre/post processing.
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
32
 *
jimw@mysql.com's avatar
jimw@mysql.com committed
33
 * PUBLIC: int __env_dbrename_pp __P((DB_ENV *, DB_TXN *,
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
34 35 36
 * PUBLIC:     const char *, const char *, const char *, u_int32_t));
 */
int
jimw@mysql.com's avatar
jimw@mysql.com committed
37
__env_dbrename_pp(dbenv, txn, name, subdb, newname, flags)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
38 39 40 41 42
	DB_ENV *dbenv;
	DB_TXN *txn;
	const char *name, *subdb, *newname;
	u_int32_t flags;
{
jimw@mysql.com's avatar
jimw@mysql.com committed
43 44 45 46 47 48
	DB *dbp;
	DB_THREAD_INFO *ip;
	int handle_check, ret, t_ret, txn_local;

	dbp = NULL;
	txn_local = 0;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
49 50 51 52

	PANIC_CHECK(dbenv);
	ENV_ILLEGAL_BEFORE_OPEN(dbenv, "DB_ENV->dbrename");

jimw@mysql.com's avatar
jimw@mysql.com committed
53 54 55 56
	/*
	 * The actual argument checking is simple, do it inline, outside of
	 * the replication block.
	 */
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
57 58 59
	if ((ret = __db_fchk(dbenv, "DB->rename", flags, DB_AUTO_COMMIT)) != 0)
		return (ret);

jimw@mysql.com's avatar
jimw@mysql.com committed
60 61 62 63 64 65 66 67 68
	ENV_ENTER(dbenv, ip);

	/* Check for replication block. */
	handle_check = IS_ENV_REPLICATED(dbenv);
	if (handle_check && (ret = __env_rep_enter(dbenv, 1)) != 0) {
		handle_check = 0;
		goto err;
	}

ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
69 70 71 72
	/*
	 * Create local transaction as necessary, check for consistent
	 * transaction usage.
	 */
jimw@mysql.com's avatar
jimw@mysql.com committed
73
	if (IS_ENV_AUTO_COMMIT(dbenv, txn, flags)) {
jimw@mysql.com's avatar
jimw@mysql.com committed
74
		if ((ret = __db_txn_auto_init(dbenv, &txn)) != 0)
jimw@mysql.com's avatar
jimw@mysql.com committed
75
			goto err;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
76
		txn_local = 1;
jimw@mysql.com's avatar
jimw@mysql.com committed
77 78 79 80 81
	} else
		if (txn != NULL && !TXN_ON(dbenv)) {
			ret = __db_not_txn_env(dbenv);
			goto err;
		}
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
82

jimw@mysql.com's avatar
jimw@mysql.com committed
83
	LF_CLR(DB_AUTO_COMMIT);
jimw@mysql.com's avatar
jimw@mysql.com committed
84 85 86 87 88

	if ((ret = db_create(&dbp, dbenv, 0)) != 0)
		goto err;

	ret = __db_rename_int(dbp, txn, name, subdb, newname);
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
89

jimw@mysql.com's avatar
jimw@mysql.com committed
90
	if (txn_local) {
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
91
		/*
jimw@mysql.com's avatar
jimw@mysql.com committed
92 93 94
		 * We created the DBP here and when we commit/abort, we'll
		 * release all the transactional locks, including the handle
		 * lock; mark the handle cleared explicitly.
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
95 96 97
		 */
		LOCK_INIT(dbp->handle_lock);
		dbp->lid = DB_LOCK_INVALIDID;
jimw@mysql.com's avatar
jimw@mysql.com committed
98 99 100 101 102 103 104 105 106
	} else if (txn != NULL) {
		/*
		 * We created this handle locally so we need to close it
		 * and clean it up.  Unfortunately, it's holding transactional
		 * locks that need to persist until the end of transaction.
		 * If we invalidate the locker id (dbp->lid), then the close
		 * won't free these locks prematurely.
		 */
		 dbp->lid = DB_LOCK_INVALIDID;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
107 108
	}

jimw@mysql.com's avatar
jimw@mysql.com committed
109 110 111
err:	if (txn_local && (t_ret =
	    __db_txn_auto_resolve(dbenv, txn, 0, ret)) != 0 && ret == 0)
		ret = t_ret;
jimw@mysql.com's avatar
jimw@mysql.com committed
112

jimw@mysql.com's avatar
jimw@mysql.com committed
113 114 115 116 117 118 119 120 121 122 123
	/*
	 * We never opened this dbp for real, so don't include a transaction
	 * handle, and use NOSYNC to avoid calling into mpool.
	 *
	 * !!!
	 * Note we're reversing the order of operations: we started the txn and
	 * then opened the DB handle; we're resolving the txn and then closing
	 * closing the DB handle -- it's safer.
	 */
	if (dbp != NULL &&
	    (t_ret = __db_close(dbp, NULL, DB_NOSYNC)) != 0 && ret == 0)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
124 125
		ret = t_ret;

jimw@mysql.com's avatar
jimw@mysql.com committed
126 127 128 129
	if (handle_check && (t_ret = __env_db_rep_exit(dbenv)) != 0 && ret == 0)
		ret = t_ret;

	ENV_LEAVE(dbenv, ip);
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
130 131 132 133
	return (ret);
}

/*
jimw@mysql.com's avatar
jimw@mysql.com committed
134 135
 * __db_rename_pp
 *	DB->rename pre/post processing.
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
136
 *
jimw@mysql.com's avatar
jimw@mysql.com committed
137
 * PUBLIC: int __db_rename_pp __P((DB *,
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
138 139 140
 * PUBLIC:     const char *, const char *, const char *, u_int32_t));
 */
int
jimw@mysql.com's avatar
jimw@mysql.com committed
141
__db_rename_pp(dbp, name, subdb, newname, flags)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
142 143 144 145 146
	DB *dbp;
	const char *name, *subdb, *newname;
	u_int32_t flags;
{
	DB_ENV *dbenv;
jimw@mysql.com's avatar
jimw@mysql.com committed
147 148
	DB_THREAD_INFO *ip;
	int handle_check, ret, t_ret;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
149 150

	dbenv = dbp->dbenv;
jimw@mysql.com's avatar
jimw@mysql.com committed
151
	handle_check = 0;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
152 153 154 155 156 157 158 159 160 161 162 163 164

	PANIC_CHECK(dbenv);

	/*
	 * Validate arguments, continuing to destroy the handle on failure.
	 *
	 * Cannot use DB_ILLEGAL_AFTER_OPEN directly because it returns.
	 *
	 * !!!
	 * We have a serious problem if we're here with a handle used to open
	 * a database -- we'll destroy the handle, and the application won't
	 * ever be able to close the database.
	 */
jimw@mysql.com's avatar
jimw@mysql.com committed
165 166
	if (F_ISSET(dbp, DB_AM_OPEN_CALLED))
		return (__db_mi_open(dbenv, "DB->rename", 1));
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
167 168 169

	/* Validate arguments. */
	if ((ret = __db_fchk(dbenv, "DB->rename", flags, 0)) != 0)
jimw@mysql.com's avatar
jimw@mysql.com committed
170
		return (ret);
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
171 172 173

	/* Check for consistent transaction usage. */
	if ((ret = __db_check_txn(dbp, NULL, DB_LOCK_INVALIDID, 0)) != 0)
jimw@mysql.com's avatar
jimw@mysql.com committed
174 175 176
		return (ret);

	ENV_ENTER(dbenv, ip);
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
177

jimw@mysql.com's avatar
jimw@mysql.com committed
178
	handle_check = IS_ENV_REPLICATED(dbenv);
jimw@mysql.com's avatar
jimw@mysql.com committed
179 180 181 182 183
	if (handle_check && (ret = __db_rep_enter(dbp, 1, 1, 0)) != 0) {
		handle_check = 0;
		goto err;
	}

ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
184
	/* Rename the file. */
jimw@mysql.com's avatar
jimw@mysql.com committed
185
	ret = __db_rename(dbp, NULL, name, subdb, newname);
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
186

jimw@mysql.com's avatar
jimw@mysql.com committed
187 188 189
	if (handle_check && (t_ret = __env_db_rep_exit(dbenv)) != 0 && ret == 0)
		ret = t_ret;
err:	ENV_LEAVE(dbenv, ip);
jimw@mysql.com's avatar
jimw@mysql.com committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
	return (ret);
}

/*
 * __db_rename
 *	DB->rename method.
 *
 * PUBLIC: int __db_rename
 * PUBLIC:     __P((DB *, DB_TXN *, const char *, const char *, const char *));
 */
int
__db_rename(dbp, txn, name, subdb, newname)
	DB *dbp;
	DB_TXN *txn;
	const char *name, *subdb, *newname;
{
	int ret, t_ret;

	ret = __db_rename_int(dbp, txn, name, subdb, newname);

	if ((t_ret = __db_close(dbp, txn, DB_NOSYNC)) != 0 && ret == 0)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
211 212 213 214 215 216
		ret = t_ret;

	return (ret);
}

/*
jimw@mysql.com's avatar
jimw@mysql.com committed
217 218 219
 * __db_rename_int
 *	Worker function for DB->rename method; the close of the dbp is
 * left in the wrapper routine.
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
220
 *
jimw@mysql.com's avatar
jimw@mysql.com committed
221 222
 * PUBLIC: int __db_rename_int
 * PUBLIC:     __P((DB *, DB_TXN *, const char *, const char *, const char *));
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
223 224
 */
int
jimw@mysql.com's avatar
jimw@mysql.com committed
225
__db_rename_int(dbp, txn, name, subdb, newname)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
226 227 228 229 230 231
	DB *dbp;
	DB_TXN *txn;
	const char *name, *subdb, *newname;
{
	DB_ENV *dbenv;
	int ret;
jimw@mysql.com's avatar
jimw@mysql.com committed
232
	char *old, *real_name;
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
233 234 235 236 237 238

	dbenv = dbp->dbenv;
	real_name = NULL;

	DB_TEST_RECOVERY(dbp, DB_TEST_PREDESTROY, ret, name);

jimw@mysql.com's avatar
jimw@mysql.com committed
239 240 241 242 243 244 245 246 247
	if (name == NULL && subdb == NULL) {
		__db_err(dbenv, "Rename on temporary files invalid");
		ret = EINVAL;
		goto err;
	}

	if (name == NULL)
		MAKE_INMEM(dbp);
	else if (subdb != NULL) {
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
248 249 250 251
		ret = __db_subdb_rename(dbp, txn, name, subdb, newname);
		goto err;
	}

jimw@mysql.com's avatar
jimw@mysql.com committed
252
	/*
jimw@mysql.com's avatar
jimw@mysql.com committed
253
	 * From here on down, this pertains to files or in-memory databases.
jimw@mysql.com's avatar
jimw@mysql.com committed
254 255 256
	 *
	 * Find the real name of the file.
	 */
jimw@mysql.com's avatar
jimw@mysql.com committed
257 258 259 260 261 262 263 264 265
	if (F_ISSET(dbp, DB_AM_INMEM)) {
		old = (char *)subdb;
		real_name = (char *)subdb;
	} else {
		if ((ret = __db_appname(dbenv,
		    DB_APP_DATA, name, 0, NULL, &real_name)) != 0)
			goto err;
		old = (char *)name;
	}
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282

	if ((ret = __fop_remove_setup(dbp, txn, real_name, 0)) != 0)
		goto err;

	if (dbp->db_am_rename != NULL &&
	    (ret = dbp->db_am_rename(dbp, txn, name, subdb, newname)) != 0)
		goto err;

	/*
	 * The transactional case and non-transactional case are
	 * quite different.  In the non-transactional case, we simply
	 * do the rename.  In the transactional case, since we need
	 * the ability to back out and maintain locking, we have to
	 * create a temporary object as a placeholder.  This is all
	 * taken care of in the fop layer.
	 */
	if (txn != NULL) {
jimw@mysql.com's avatar
jimw@mysql.com committed
283
		if ((ret = __fop_dummy(dbp, txn, old, newname, 0)) != 0)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
284 285
			goto err;
	} else {
jimw@mysql.com's avatar
jimw@mysql.com committed
286
		if ((ret = __fop_dbrename(dbp, old, newname)) != 0)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
287 288 289 290 291 292 293 294 295 296 297 298 299
			goto err;
	}

	/*
	 * I am pretty sure that we haven't gotten a dbreg id, so calling
	 * dbreg_filelist_update is not necessary.
	 */
	DB_ASSERT(dbp->log_filename == NULL ||
	    dbp->log_filename->id == DB_LOGFILEID_INVALID);

	DB_TEST_RECOVERY(dbp, DB_TEST_POSTDESTROY, ret, newname);

DB_TEST_RECOVERY_LABEL
jimw@mysql.com's avatar
jimw@mysql.com committed
300
err:	if (!F_ISSET(dbp, DB_AM_INMEM) && real_name != NULL)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
		__os_free(dbenv, real_name);

	return (ret);
}

/*
 * __db_subdb_rename --
 *	Rename a subdatabase.
 */
static int
__db_subdb_rename(dbp, txn, name, subdb, newname)
	DB *dbp;
	DB_TXN *txn;
	const char *name, *subdb, *newname;
{
	DB *mdbp;
	DB_ENV *dbenv;
	PAGE *meta;
	int ret, t_ret;

	mdbp = NULL;
	meta = NULL;
	dbenv = dbp->dbenv;

	/*
	 * We have not opened this dbp so it isn't marked as a subdb,
	 * but it ought to be.
	 */
	F_SET(dbp, DB_AM_SUBDB);

	/*
	 * Rename the entry in the main database.  We need to first
	 * get the meta-data page number (via MU_OPEN) so that we can
	 * read the meta-data page and obtain a handle lock.  Once we've
	 * done that, we can proceed to do the rename in the master.
	 */
	if ((ret = __db_master_open(dbp, txn, name, 0, 0, &mdbp)) != 0)
		goto err;

	if ((ret = __db_master_update(mdbp, dbp, txn, subdb, dbp->type,
	    MU_OPEN, NULL, 0)) != 0)
		goto err;

jimw@mysql.com's avatar
jimw@mysql.com committed
344
	if ((ret = __memp_fget(mdbp->mpf, &dbp->meta_pgno, 0, &meta)) != 0)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
345
		goto err;
jimw@mysql.com's avatar
jimw@mysql.com committed
346
	memcpy(dbp->fileid, ((DBMETA *)meta)->uid, DB_FILE_ID_LEN);
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
347
	if ((ret = __fop_lock_handle(dbenv,
jimw@mysql.com's avatar
jimw@mysql.com committed
348
	    dbp, mdbp->lid, DB_LOCK_WRITE, NULL, NOWAIT_FLAG(txn))) != 0)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
349 350
		goto err;

jimw@mysql.com's avatar
jimw@mysql.com committed
351
	ret = __memp_fput(mdbp->mpf, meta, 0);
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
352 353 354 355 356 357 358 359 360 361 362 363 364
	meta = NULL;
	if (ret != 0)
		goto err;

	if ((ret = __db_master_update(mdbp, dbp, txn,
	    subdb, dbp->type, MU_RENAME, newname, 0)) != 0)
		goto err;

	DB_TEST_RECOVERY(dbp, DB_TEST_POSTDESTROY, ret, name);

DB_TEST_RECOVERY_LABEL
err:
	if (meta != NULL &&
jimw@mysql.com's avatar
jimw@mysql.com committed
365
	    (t_ret = __memp_fput(mdbp->mpf, meta, 0)) != 0 && ret == 0)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
366 367 368
		ret = t_ret;

	if (mdbp != NULL &&
jimw@mysql.com's avatar
jimw@mysql.com committed
369
	    (t_ret = __db_close(mdbp, txn, DB_NOSYNC)) != 0 && ret == 0)
ram@mysql.r18.ru's avatar
ram@mysql.r18.ru committed
370 371 372 373
		ret = t_ret;

	return (ret);
}