summaryrefslogtreecommitdiff
path: root/src/txn/txn_nsnap.c
diff options
context:
space:
mode:
authorMichael Cahill <michael.cahill@mongodb.com>2015-09-25 09:47:19 +1000
committerMichael Cahill <michael.cahill@mongodb.com>2015-09-29 16:56:36 +1000
commit4b6157e191d644603e6c0642aed1e9f07eee596d (patch)
treea3bc4859b2dac87d4cbc571e8b9fefe2377f49c5 /src/txn/txn_nsnap.c
parentd1dff562e256cf3726f8ec229feced879f212096 (diff)
downloadmongo-4b6157e191d644603e6c0642aed1e9f07eee596d.tar.gz
WT-2112 Use bool in more places
Merge pull request #2216 from wiredtiger/more-bool (cherry picked from commit 53638a10713925406f92d20fa840a94cd2cfa22a)
Diffstat (limited to 'src/txn/txn_nsnap.c')
-rw-r--r--src/txn/txn_nsnap.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/txn/txn_nsnap.c b/src/txn/txn_nsnap.c
new file mode 100644
index 00000000000..a5ec9cb9b82
--- /dev/null
+++ b/src/txn/txn_nsnap.c
@@ -0,0 +1,367 @@
+/*-
+ * Copyright (c) 2014-2015 MongoDB, Inc.
+ * Copyright (c) 2008-2014 WiredTiger, Inc.
+ * All rights reserved.
+ *
+ * See the file LICENSE for redistribution information.
+ */
+
+#include "wt_internal.h"
+
+/*
+ * __nsnap_destroy --
+ * Destroy a named snapshot structure.
+ */
+static void
+__nsnap_destroy(WT_SESSION_IMPL *session, WT_NAMED_SNAPSHOT *nsnap)
+{
+ __wt_free(session, nsnap->name);
+ __wt_free(session, nsnap->snapshot);
+ __wt_free(session, nsnap);
+}
+
+/*
+ * __nsnap_drop_one --
+ * Drop a single named snapshot. The named snapshot lock must be held
+ * write locked.
+ */
+static int
+__nsnap_drop_one(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *name)
+{
+ WT_DECL_RET;
+ WT_NAMED_SNAPSHOT *found;
+ WT_TXN_GLOBAL *txn_global;
+
+ txn_global = &S2C(session)->txn_global;
+
+ TAILQ_FOREACH(found, &txn_global->nsnaph, q)
+ if (WT_STRING_MATCH(found->name, name->str, name->len))
+ break;
+
+ if (found == NULL)
+ return (WT_NOTFOUND);
+
+ /* Bump the global ID if we are removing the first entry */
+ if (found == TAILQ_FIRST(&txn_global->nsnaph))
+ txn_global->nsnap_oldest_id = (TAILQ_NEXT(found, q) != NULL) ?
+ TAILQ_NEXT(found, q)->snap_min : WT_TXN_NONE;
+ TAILQ_REMOVE(&txn_global->nsnaph, found, q);
+ __nsnap_destroy(session, found);
+
+ return (ret);
+}
+
+/*
+ * __nsnap_drop_to --
+ * Drop named snapshots, if the name is NULL all snapshots will be
+ * dropped. The named snapshot lock must be held write locked.
+ */
+static int
+__nsnap_drop_to(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *name, bool inclusive)
+{
+ WT_DECL_RET;
+ WT_NAMED_SNAPSHOT *last, *nsnap, *prev;
+ WT_TXN_GLOBAL *txn_global;
+ uint64_t new_nsnap_oldest;
+
+ last = nsnap = prev = NULL;
+ txn_global = &S2C(session)->txn_global;
+
+ if (TAILQ_EMPTY(&txn_global->nsnaph)) {
+ if (name == NULL)
+ return (0);
+ /*
+ * Dropping specific snapshots when there aren't any it's an
+ * error.
+ */
+ WT_RET_MSG(session, EINVAL,
+ "Named snapshot '%.*s' for drop not found",
+ (int)name->len, name->str);
+ }
+
+ /*
+ * The new ID will be none if we are removing all named snapshots
+ * which is the default behavior of this loop.
+ */
+ new_nsnap_oldest = WT_TXN_NONE;
+ if (name != NULL) {
+ TAILQ_FOREACH(last, &txn_global->nsnaph, q) {
+ if (WT_STRING_MATCH(last->name, name->str, name->len))
+ break;
+ prev = last;
+ }
+ if (last == NULL)
+ WT_RET_MSG(session, EINVAL,
+ "Named snapshot '%.*s' for drop not found",
+ (int)name->len, name->str);
+
+ if (!inclusive) {
+ /* We are done if a drop before points to the head */
+ if (prev == 0)
+ return (0);
+ last = prev;
+ }
+
+ if (TAILQ_NEXT(last, q) != NULL)
+ new_nsnap_oldest = TAILQ_NEXT(last, q)->snap_min;
+ }
+
+ do {
+ nsnap = TAILQ_FIRST(&txn_global->nsnaph);
+ WT_ASSERT(session, nsnap != NULL);
+ TAILQ_REMOVE(&txn_global->nsnaph, nsnap, q);
+ __nsnap_destroy(session, nsnap);
+ /* Last will be NULL in the all case so it will never match */
+ } while (nsnap != last && !TAILQ_EMPTY(&txn_global->nsnaph));
+
+ /* Now that the queue of named snapshots is updated, update the ID */
+ txn_global->nsnap_oldest_id = new_nsnap_oldest;
+
+ return (ret);
+}
+
+/*
+ * __wt_txn_named_snapshot_begin --
+ * Begin an named in-memory snapshot.
+ */
+int
+__wt_txn_named_snapshot_begin(WT_SESSION_IMPL *session, const char *cfg[])
+{
+ WT_CONFIG_ITEM cval;
+ WT_DECL_RET;
+ WT_NAMED_SNAPSHOT *nsnap, *nsnap_new;
+ WT_TXN *txn;
+ WT_TXN_GLOBAL *txn_global;
+ const char *txn_cfg[] =
+ { WT_CONFIG_BASE(session, WT_SESSION_begin_transaction),
+ "isolation=snapshot", NULL };
+ bool started_txn;
+
+ started_txn = false;
+ nsnap_new = NULL;
+ txn_global = &S2C(session)->txn_global;
+ txn = &session->txn;
+
+ WT_RET(__wt_config_gets_def(session, cfg, "name", 0, &cval));
+ WT_ASSERT(session, cval.len != 0);
+
+ if (!F_ISSET(txn, WT_TXN_RUNNING)) {
+ WT_RET(__wt_txn_begin(session, txn_cfg));
+ started_txn = true;
+ }
+ F_SET(txn, WT_TXN_READONLY);
+
+ /* Save a copy of the transaction's snapshot. */
+ WT_ERR(__wt_calloc_one(session, &nsnap_new));
+ nsnap = nsnap_new;
+ WT_ERR(__wt_strndup(session, cval.str, cval.len, &nsnap->name));
+ nsnap->snap_min = txn->snap_min;
+ nsnap->snap_max = txn->snap_max;
+ if (txn->snapshot_count > 0) {
+ WT_ERR(__wt_calloc_def(
+ session, txn->snapshot_count, &nsnap->snapshot));
+ memcpy(nsnap->snapshot, txn->snapshot,
+ txn->snapshot_count * sizeof(*nsnap->snapshot));
+ }
+ nsnap->snapshot_count = txn->snapshot_count;
+
+ /* Update the list. */
+
+ /*
+ * The semantic is that a new snapshot with the same name as an
+ * existing snapshot will replace the old one.
+ */
+ WT_ERR_NOTFOUND_OK(__nsnap_drop_one(session, &cval));
+
+ if (TAILQ_EMPTY(&txn_global->nsnaph))
+ txn_global->nsnap_oldest_id = nsnap_new->snap_min;
+ TAILQ_INSERT_TAIL(&txn_global->nsnaph, nsnap_new, q);
+ nsnap_new = NULL;
+
+err: if (started_txn)
+ WT_TRET(__wt_txn_rollback(session, NULL));
+ else if (ret == 0)
+ F_SET(txn, WT_TXN_NAMED_SNAPSHOT);
+
+ if (nsnap_new != NULL)
+ __nsnap_destroy(session, nsnap_new);
+
+ return (ret);
+}
+
+/*
+ * __wt_txn_named_snapshot_drop --
+ * Drop named snapshots
+ */
+int
+__wt_txn_named_snapshot_drop(WT_SESSION_IMPL *session, const char *cfg[])
+{
+ WT_CONFIG objectconf;
+ WT_CONFIG_ITEM all_config, k, names_config, to_config, before_config, v;
+ WT_DECL_RET;
+
+ WT_RET(__wt_config_gets_def(session, cfg, "drop.all", 0, &all_config));
+ WT_RET(__wt_config_gets_def(
+ session, cfg, "drop.names", 0, &names_config));
+ WT_RET(__wt_config_gets_def(session, cfg, "drop.to", 0, &to_config));
+ WT_RET(__wt_config_gets_def(
+ session, cfg, "drop.before", 0, &before_config));
+
+ if (all_config.val != 0)
+ WT_RET(__nsnap_drop_to(session, NULL, true));
+ else if (before_config.len != 0)
+ WT_RET(__nsnap_drop_to(session, &before_config, false));
+ else if (to_config.len != 0)
+ WT_RET(__nsnap_drop_to(session, &to_config, true));
+
+ /* We are done if there are no named drops */
+
+ if (names_config.len != 0) {
+ WT_RET(__wt_config_subinit(
+ session, &objectconf, &names_config));
+ while ((ret = __wt_config_next(&objectconf, &k, &v)) == 0) {
+ ret = __nsnap_drop_one(session, &k);
+ if (ret != 0)
+ WT_RET_MSG(session, EINVAL,
+ "Named snapshot '%.*s' for drop not found",
+ (int)k.len, k.str);
+ }
+ if (ret == WT_NOTFOUND)
+ ret = 0;
+ }
+
+ return (ret);
+}
+
+/*
+ * __wt_txn_named_snapshot_get --
+ * Lookup a named snapshot for a transaction.
+ */
+int
+__wt_txn_named_snapshot_get(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *nameval)
+{
+ WT_NAMED_SNAPSHOT *nsnap;
+ WT_TXN *txn;
+ WT_TXN_GLOBAL *txn_global;
+ WT_TXN_STATE *txn_state;
+
+ txn = &session->txn;
+ txn_global = &S2C(session)->txn_global;
+ txn_state = WT_SESSION_TXN_STATE(session);
+
+ txn->isolation = WT_ISO_SNAPSHOT;
+ if (session->ncursors > 0)
+ WT_RET(__wt_session_copy_values(session));
+
+ WT_RET(__wt_readlock(session, txn_global->nsnap_rwlock));
+ TAILQ_FOREACH(nsnap, &txn_global->nsnaph, q)
+ if (WT_STRING_MATCH(nsnap->name, nameval->str, nameval->len)) {
+ txn->snap_min = txn_state->snap_min = nsnap->snap_min;
+ txn->snap_max = nsnap->snap_max;
+ if ((txn->snapshot_count = nsnap->snapshot_count) != 0)
+ memcpy(txn->snapshot, nsnap->snapshot,
+ nsnap->snapshot_count *
+ sizeof(*nsnap->snapshot));
+ F_SET(txn, WT_TXN_HAS_SNAPSHOT);
+ break;
+ }
+ WT_RET(__wt_readunlock(session, txn_global->nsnap_rwlock));
+
+ if (nsnap == NULL)
+ WT_RET_MSG(session, EINVAL,
+ "Named snapshot '%.*s' not found",
+ (int)nameval->len, nameval->str);
+
+ /* Flag that this transaction is opened on a named snapshot */
+ F_SET(txn, WT_TXN_NAMED_SNAPSHOT);
+
+ return (0);
+}
+
+/*
+ * __wt_txn_named_snapshot_config --
+ * Check the configuration for a named snapshot
+ */
+int
+__wt_txn_named_snapshot_config(WT_SESSION_IMPL *session,
+ const char *cfg[], bool *has_create, bool *has_drops)
+{
+ WT_CONFIG_ITEM cval;
+ WT_CONFIG_ITEM all_config, names_config, to_config, before_config;
+ WT_TXN *txn;
+
+ txn = &session->txn;
+ *has_create = *has_drops = false;
+
+ /* Verify that the name is legal. */
+ WT_RET(__wt_config_gets_def(session, cfg, "name", 0, &cval));
+ if (cval.len != 0) {
+ if (WT_STRING_MATCH("all", cval.str, cval.len))
+ WT_RET_MSG(session, EINVAL,
+ "Can't create snapshot with reserved \"all\" name");
+
+ WT_RET(__wt_name_check(session, cval.str, cval.len));
+
+ if (F_ISSET(txn, WT_TXN_RUNNING) &&
+ txn->isolation != WT_ISO_SNAPSHOT)
+ WT_RET_MSG(session, EINVAL,
+ "Can't create a named snapshot from a running "
+ "transaction that isn't snapshot isolation");
+ else if (F_ISSET(txn, WT_TXN_RUNNING) && txn->mod_count != 0)
+ WT_RET_MSG(session, EINVAL,
+ "Can't create a named snapshot from a running "
+ "transaction that has made updates");
+ *has_create = true;
+ }
+
+ /* Verify that the drop configuration is sane. */
+ WT_RET(__wt_config_gets_def(session, cfg, "drop.all", 0, &all_config));
+ WT_RET(__wt_config_gets_def(
+ session, cfg, "drop.names", 0, &names_config));
+ WT_RET(__wt_config_gets_def(session, cfg, "drop.to", 0, &to_config));
+ WT_RET(__wt_config_gets_def(
+ session, cfg, "drop.before", 0, &before_config));
+
+ /* Avoid more work if no drops are configured. */
+ if (all_config.val != 0 || names_config.len != 0 ||
+ before_config.len != 0 || to_config.len != 0) {
+ if (before_config.len != 0 && to_config.len != 0)
+ WT_RET_MSG(session, EINVAL,
+ "Illegal configuration; named snapshot drop can't "
+ "specify both before and to options");
+ if (all_config.val != 0 && (names_config.len != 0 ||
+ to_config.len != 0 || before_config.len != 0))
+ WT_RET_MSG(session, EINVAL,
+ "Illegal configuration; named snapshot drop can't "
+ "specify all and any other options");
+ *has_drops = true;
+ }
+
+ if (!*has_create && !*has_drops)
+ WT_RET_MSG(session, EINVAL,
+ "WT_SESSION::snapshot API called without any drop or "
+ "name option.");
+
+ return (0);
+}
+
+/*
+ * __wt_txn_named_snapshot_destroy --
+ * Destroy all named snapshots on connection close
+ */
+int
+__wt_txn_named_snapshot_destroy(WT_SESSION_IMPL *session)
+{
+ WT_NAMED_SNAPSHOT *nsnap;
+ WT_TXN_GLOBAL *txn_global;
+
+ txn_global = &S2C(session)->txn_global;
+ txn_global->nsnap_oldest_id = WT_TXN_NONE;
+
+ while ((nsnap = TAILQ_FIRST(&txn_global->nsnaph)) != NULL) {
+ TAILQ_REMOVE(&txn_global->nsnaph, nsnap, q);
+ __nsnap_destroy(session, nsnap);
+ }
+
+ return (0);
+}