summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Bostic <keith.bostic@mongodb.com>2017-03-24 01:02:52 -0400
committerMichael Cahill <michael.cahill@mongodb.com>2017-03-24 16:02:52 +1100
commit54909d4c49019e6d9d007d3783cb8f3dbbccba84 (patch)
tree584387cc1dce5457775fdb0aa0be10c85e914904
parente4edaa7b73ca8583506f23a0c6fe701d6213d836 (diff)
downloadmongo-54909d4c49019e6d9d007d3783cb8f3dbbccba84.tar.gz
WT-98 Update the current cursor value without a search (#3330)
-rw-r--r--src/btree/bt_curnext.c11
-rw-r--r--src/btree/bt_curprev.c11
-rw-r--r--src/btree/bt_cursor.c324
-rw-r--r--src/btree/bt_random.c7
-rw-r--r--src/btree/bt_ret.c21
-rw-r--r--src/cursor/cur_file.c135
-rw-r--r--src/cursor/cur_join.c4
-rw-r--r--src/evict/evict_lru.c4
-rw-r--r--src/include/cursor.i25
-rw-r--r--src/include/extern.h4
-rw-r--r--src/include/packing.i2
-rw-r--r--src/include/wiredtiger.in4
-rw-r--r--src/log/log.c4
-rw-r--r--src/lsm/lsm_cursor.c35
-rw-r--r--src/lsm/lsm_merge.c2
-rw-r--r--src/lsm/lsm_meta.c2
-rw-r--r--src/lsm/lsm_stat.c4
-rw-r--r--src/schema/schema_create.c2
-rw-r--r--src/schema/schema_worker.c4
-rw-r--r--src/session/session_api.c7
-rw-r--r--src/session/session_compact.c2
-rw-r--r--src/txn/txn.c2
-rw-r--r--src/txn/txn_ckpt.c2
-rw-r--r--test/format/config.c47
-rw-r--r--test/format/ops.c362
-rw-r--r--test/suite/test_truncate01.py1
26 files changed, 663 insertions, 365 deletions
diff --git a/src/btree/bt_curnext.c b/src/btree/bt_curnext.c
index ba5fceae7c7..21e575ffca9 100644
--- a/src/btree/bt_curnext.c
+++ b/src/btree/bt_curnext.c
@@ -579,20 +579,20 @@ __wt_btcur_iterate_setup(WT_CURSOR_BTREE *cbt)
int
__wt_btcur_next(WT_CURSOR_BTREE *cbt, bool truncating)
{
+ WT_CURSOR *cursor;
WT_DECL_RET;
WT_PAGE *page;
WT_SESSION_IMPL *session;
uint32_t flags;
bool newpage;
+ cursor = &cbt->iface;
session = (WT_SESSION_IMPL *)cbt->iface.session;
WT_STAT_CONN_INCR(session, cursor_next);
WT_STAT_DATA_INCR(session, cursor_next);
- flags = WT_READ_SKIP_INTL; /* Tree walk flags. */
- if (truncating)
- LF_SET(WT_READ_TRUNCATE);
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
WT_RET(__cursor_func_init(cbt, false));
@@ -608,6 +608,9 @@ __wt_btcur_next(WT_CURSOR_BTREE *cbt, bool truncating)
* found. Then, move to the next page, until we reach the end of the
* file.
*/
+ flags = WT_READ_SKIP_INTL; /* tree walk flags */
+ if (truncating)
+ LF_SET(WT_READ_TRUNCATE);
for (newpage = false;; newpage = true) {
page = cbt->ref == NULL ? NULL : cbt->ref->page;
@@ -676,6 +679,8 @@ __wt_btcur_next(WT_CURSOR_BTREE *cbt, bool truncating)
if (ret == 0)
WT_ERR(__wt_cursor_key_order_check(session, cbt, true));
#endif
+ if (ret == 0)
+ F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
err: if (ret != 0)
WT_TRET(__cursor_reset(cbt));
diff --git a/src/btree/bt_curprev.c b/src/btree/bt_curprev.c
index 602c01b60eb..bf4bdad6529 100644
--- a/src/btree/bt_curprev.c
+++ b/src/btree/bt_curprev.c
@@ -535,20 +535,20 @@ new_insert: if ((ins = cbt->ins) != NULL) {
int
__wt_btcur_prev(WT_CURSOR_BTREE *cbt, bool truncating)
{
+ WT_CURSOR *cursor;
WT_DECL_RET;
WT_PAGE *page;
WT_SESSION_IMPL *session;
uint32_t flags;
bool newpage;
+ cursor = &cbt->iface;
session = (WT_SESSION_IMPL *)cbt->iface.session;
WT_STAT_CONN_INCR(session, cursor_prev);
WT_STAT_DATA_INCR(session, cursor_prev);
- flags = WT_READ_PREV | WT_READ_SKIP_INTL; /* Tree walk flags. */
- if (truncating)
- LF_SET(WT_READ_TRUNCATE);
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
WT_RET(__cursor_func_init(cbt, false));
@@ -564,6 +564,9 @@ __wt_btcur_prev(WT_CURSOR_BTREE *cbt, bool truncating)
* found. Then, move to the previous page, until we reach the start
* of the file.
*/
+ flags = WT_READ_PREV | WT_READ_SKIP_INTL; /* tree walk flags */
+ if (truncating)
+ LF_SET(WT_READ_TRUNCATE);
for (newpage = false;; newpage = true) {
page = cbt->ref == NULL ? NULL : cbt->ref->page;
@@ -631,6 +634,8 @@ __wt_btcur_prev(WT_CURSOR_BTREE *cbt, bool truncating)
if (ret == 0)
WT_ERR(__wt_cursor_key_order_check(session, cbt, false));
#endif
+ if (ret == 0)
+ F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
err: if (ret != 0)
WT_TRET(__cursor_reset(cbt));
diff --git a/src/btree/bt_cursor.c b/src/btree/bt_cursor.c
index 48ae1ad6d76..944e276fc01 100644
--- a/src/btree/bt_cursor.c
+++ b/src/btree/bt_cursor.c
@@ -9,32 +9,46 @@
#include "wt_internal.h"
/*
- * WT_CURFILE_OP_XXX
- * If we're going to return an error, we need to restore the cursor to
- * a valid state, the upper-level cursor code is likely to retry. The macros
- * here are called to save and restore that state.
+ * When returning an error, we need to restore the cursor to a valid state, the
+ * upper-level cursor code is likely to retry. This structure and the associated
+ * functions are used save and restore the cursor state.
*/
-#define WT_CURFILE_OP_DECL \
- WT_ITEM __key_copy; \
- WT_ITEM __value_copy; \
- uint64_t __recno; \
- uint32_t __flags
-#define WT_CURFILE_OP_PUSH do { \
- WT_ITEM_SET(__key_copy, cursor->key); \
- WT_ITEM_SET(__value_copy, cursor->value); \
- __recno = cursor->recno; \
- __flags = cursor->flags; \
-} while (0)
-#define WT_CURFILE_OP_POP do { \
- cursor->recno = __recno; \
- if (FLD_ISSET(__flags, WT_CURSTD_KEY_EXT)) \
- WT_ITEM_SET(cursor->key, __key_copy); \
- if (FLD_ISSET(__flags, WT_CURSTD_VALUE_EXT)) \
- WT_ITEM_SET(cursor->value, __value_copy); \
- F_CLR(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT); \
- F_SET(cursor, \
- FLD_MASK(__flags, WT_CURSTD_KEY_EXT | WT_CURSTD_VALUE_EXT));\
-} while (0)
+typedef struct {
+ WT_ITEM key;
+ WT_ITEM value;
+ uint64_t recno;
+ uint32_t flags;
+} WT_CURFILE_STATE;
+
+/*
+ * __cursor_state_save --
+ * Save the cursor's external state.
+ */
+static inline void
+__cursor_state_save(WT_CURSOR *cursor, WT_CURFILE_STATE *state)
+{
+ WT_ITEM_SET(state->key, cursor->key);
+ WT_ITEM_SET(state->value, cursor->value);
+ state->recno = cursor->recno;
+ state->flags = cursor->flags;
+}
+
+/*
+ * __cursor_state_restore --
+ * Restore the cursor's external state.
+ */
+static inline void
+__cursor_state_restore(WT_CURSOR *cursor, WT_CURFILE_STATE *state)
+{
+ if (F_ISSET(state, WT_CURSTD_KEY_EXT))
+ WT_ITEM_SET(cursor->key, state->key);
+ if (F_ISSET(state, WT_CURSTD_VALUE_EXT))
+ WT_ITEM_SET(cursor->value, state->value);
+ cursor->recno = state->recno;
+ F_CLR(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
+ F_SET(cursor, F_MASK(state, WT_CURSTD_KEY_EXT | WT_CURSTD_VALUE_EXT));
+
+}
/*
* __cursor_page_pinned --
@@ -377,13 +391,17 @@ __cursor_row_modify(
int
__wt_btcur_reset(WT_CURSOR_BTREE *cbt)
{
+ WT_CURSOR *cursor;
WT_SESSION_IMPL *session;
+ cursor = &cbt->iface;
session = (WT_SESSION_IMPL *)cbt->iface.session;
WT_STAT_CONN_INCR(session, cursor_reset);
WT_STAT_DATA_INCR(session, cursor_reset);
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+
return (__cursor_reset(cbt));
}
@@ -395,6 +413,7 @@ int
__wt_btcur_search(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
@@ -409,6 +428,15 @@ __wt_btcur_search(WT_CURSOR_BTREE *cbt)
WT_STAT_CONN_INCR(session, cursor_search);
WT_STAT_DATA_INCR(session, cursor_search);
+ __cursor_state_save(cursor, &state);
+
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key, then re-save the cursor state.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+
/*
* If we have a page pinned, search it; if we don't have a page pinned,
* or the search of the pinned page doesn't find an exact match, search
@@ -443,6 +471,8 @@ __wt_btcur_search(WT_CURSOR_BTREE *cbt)
cbt->v = 0;
cursor->value.data = &cbt->v;
cursor->value.size = 1;
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
} else
ret = WT_NOTFOUND;
@@ -451,8 +481,10 @@ __wt_btcur_search(WT_CURSOR_BTREE *cbt)
WT_ERR(__wt_cursor_key_order_init(session, cbt));
#endif
-err: if (ret != 0)
+err: if (ret != 0) {
WT_TRET(__cursor_reset(cbt));
+ __cursor_state_restore(cursor, &state);
+ }
return (ret);
}
@@ -464,6 +496,7 @@ int
__wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
@@ -480,6 +513,15 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
WT_STAT_CONN_INCR(session, cursor_search_near);
WT_STAT_DATA_INCR(session, cursor_search_near);
+ __cursor_state_save(cursor, &state);
+
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key, then re-save the cursor state.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+
/*
* If we have a row-store page pinned, search it; if we don't have a
* page pinned, or the search of the pinned page doesn't find an exact
@@ -544,6 +586,8 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
cursor->value.data = &cbt->v;
cursor->value.size = 1;
exact = 0;
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
} else if ((ret = __wt_btcur_next(cbt, false)) != WT_NOTFOUND)
exact = 1;
else {
@@ -558,15 +602,18 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
exact = -1;
}
+err: if (ret == 0 && exactp != NULL)
+ *exactp = exact;
+
#ifdef HAVE_DIAGNOSTIC
if (ret == 0)
- WT_ERR(__wt_cursor_key_order_init(session, cbt));
+ WT_TRET(__wt_cursor_key_order_init(session, cbt));
#endif
-err: if (ret != 0)
+ if (ret != 0) {
WT_TRET(__cursor_reset(cbt));
- if (exactp != NULL && (ret == 0 || ret == WT_NOTFOUND))
- *exactp = exact;
+ __cursor_state_restore(cursor, &state);
+ }
return (ret);
}
@@ -578,9 +625,11 @@ int
__wt_btcur_insert(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
+ bool append_key;
btree = cbt->btree;
cursor = &cbt->iface;
@@ -591,6 +640,8 @@ __wt_btcur_insert(WT_CURSOR_BTREE *cbt)
WT_STAT_DATA_INCRV(session,
cursor_insert_bytes, cursor->key.size + cursor->value.size);
+ __cursor_state_save(cursor, &state);
+
if (btree->type == BTREE_ROW)
WT_RET(__cursor_size_chk(session, &cursor->key));
WT_RET(__cursor_size_chk(session, &cursor->value));
@@ -598,7 +649,58 @@ __wt_btcur_insert(WT_CURSOR_BTREE *cbt)
/* It's no longer possible to bulk-load into the tree. */
__cursor_disable_bulk(session, btree);
-retry: WT_RET(__cursor_func_init(cbt, true));
+ /*
+ * Insert a new record if WT_CURSTD_APPEND configured, (ignoring any
+ * application set record number). Although append can't be configured
+ * for a row-store, this code would break if it were, and that's owned
+ * by the upper cursor layer, be cautious.
+ */
+ append_key =
+ F_ISSET(cursor, WT_CURSTD_APPEND) && btree->type != BTREE_ROW;
+
+ /*
+ * If inserting with overwrite configured, and positioned to an on-page
+ * key, the update doesn't require another search. The cursor won't be
+ * positioned on a page with an external key set, but be sure. Cursors
+ * configured for append aren't included, regardless of whether or not
+ * they meet all other criteria.
+ */
+ if (__cursor_page_pinned(cbt) &&
+ F_ISSET_ALL(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_OVERWRITE) &&
+ !append_key) {
+ WT_ERR(__wt_txn_autocommit_check(session));
+ /*
+ * The cursor position may not be exact (the cursor's comparison
+ * value not equal to zero). Correct to an exact match so we can
+ * update whatever we're pointing at.
+ */
+ cbt->compare = 0;
+ ret = btree->type == BTREE_ROW ?
+ __cursor_row_modify(session, cbt, false) :
+ __cursor_col_modify(session, cbt, false);
+ if (ret == 0)
+ goto done;
+
+ /*
+ * The pinned page goes away if we fail for any reason, make
+ * sure there's a local copy of any key. (Restart could still
+ * use the pinned page, but that's an unlikely path.) Re-save
+ * the cursor state: we may retry but eventually fail.
+ */
+ WT_TRET(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+ goto err;
+ }
+
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key. Re-save the cursor state: we may retry but
+ * eventually fail.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+
+retry: WT_ERR(__cursor_func_init(cbt, true));
if (btree->type == BTREE_ROW) {
WT_ERR(__cursor_row_search(session, cbt, NULL, true));
@@ -613,11 +715,11 @@ retry: WT_RET(__cursor_func_init(cbt, true));
ret = __cursor_row_modify(session, cbt, false);
} else {
/*
- * If WT_CURSTD_APPEND is set, insert a new record (ignoring
- * the application's record number). The real record number
- * is assigned by the serialized append operation.
+ * Optionally insert a new record (ignoring the application's
+ * record number). The real record number is allocated by the
+ * serialized append operation.
*/
- if (F_ISSET(cursor, WT_CURSTD_APPEND))
+ if (append_key)
cbt->iface.recno = WT_RECNO_OOB;
WT_ERR(__cursor_col_search(session, cbt, NULL));
@@ -634,7 +736,8 @@ retry: WT_RET(__cursor_func_init(cbt, true));
WT_ERR(WT_DUPLICATE_KEY);
WT_ERR(__cursor_col_modify(session, cbt, false));
- if (F_ISSET(cursor, WT_CURSTD_APPEND))
+
+ if (append_key)
cbt->iface.recno = cbt->recno;
}
@@ -644,8 +747,16 @@ err: if (ret == WT_RESTART) {
goto retry;
}
- /* Insert doesn't maintain a position across calls, clear resources. */
+done: /* Insert doesn't maintain a position across calls, clear resources. */
+ if (ret == 0) {
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ if (append_key)
+ F_SET(cursor, WT_CURSTD_KEY_INT);
+ }
WT_TRET(__cursor_reset(cbt));
+ if (ret != 0)
+ __cursor_state_restore(cursor, &state);
+
return (ret);
}
@@ -681,16 +792,15 @@ __curfile_update_check(WT_CURSOR_BTREE *cbt)
}
/*
- * __wt_btcur_update_check --
+ * __wt_btcur_insert_check --
* Check whether an update would conflict.
*
- * This can be used to replace WT_CURSOR::insert or WT_CURSOR::update, so
- * they only check for conflicts without updating the tree. It is used to
- * maintain snapshot isolation for transactions that span multiple chunks
- * in an LSM tree.
+ * This can replace WT_CURSOR::insert, so it only checks for conflicts without
+ * updating the tree. It is used to maintain snapshot isolation for transactions
+ * that span multiple chunks in an LSM tree.
*/
int
-__wt_btcur_update_check(WT_CURSOR_BTREE *cbt)
+__wt_btcur_insert_check(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
WT_CURSOR *cursor;
@@ -701,14 +811,20 @@ __wt_btcur_update_check(WT_CURSOR_BTREE *cbt)
btree = cbt->btree;
session = (WT_SESSION_IMPL *)cursor->session;
-retry: WT_RET(__cursor_func_init(cbt, true));
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key. Unlike most of the btree cursor routines,
+ * we don't have to save/restore the cursor key state, none of the
+ * work done here changes the key state.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+
+retry: WT_ERR(__cursor_func_init(cbt, true));
if (btree->type == BTREE_ROW) {
WT_ERR(__cursor_row_search(session, cbt, NULL, true));
- /*
- * Just check for conflicts.
- */
+ /* Just check for conflicts. */
ret = __curfile_update_check(cbt);
} else
WT_ERR(__wt_illegal_value(session, NULL));
@@ -720,7 +836,10 @@ err: if (ret == WT_RESTART) {
}
/* Insert doesn't maintain a position across calls, clear resources. */
+ if (ret == 0)
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
WT_TRET(__cursor_reset(cbt));
+
return (ret);
}
@@ -732,7 +851,7 @@ int
__wt_btcur_remove(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
- WT_CURFILE_OP_DECL;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
@@ -742,26 +861,27 @@ __wt_btcur_remove(WT_CURSOR_BTREE *cbt)
cursor = &cbt->iface;
session = (WT_SESSION_IMPL *)cursor->session;
- WT_CURFILE_OP_PUSH;
-
WT_STAT_CONN_INCR(session, cursor_remove);
WT_STAT_DATA_INCR(session, cursor_remove);
WT_STAT_DATA_INCRV(session, cursor_remove_bytes, cursor->key.size);
+ __cursor_state_save(cursor, &state);
+
/*
* WT_CURSOR.remove has a unique semantic, the cursor stays positioned
* if it starts positioned, otherwise clear the cursor on completion.
*/
positioned = F_ISSET(cursor, WT_CURSTD_KEY_INT);
-retry:
/*
- * If removing with overwrite configured, and positioned to an on-page
- * key, the update doesn't require another search. The cursor won't be
- * positioned on a page with an external key set, but be sure.
+ * If remove positioned to an on-page key, the remove doesn't require
+ * another search. We don't care about the "overwrite" configuration
+ * because regardless of the overwrite setting, any existing record is
+ * removed, and the record must exist with a positioned cursor. The
+ * cursor won't be positioned on a page with an external key set, but
+ * be sure.
*/
- if (__cursor_page_pinned(cbt) &&
- F_ISSET_ALL(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_OVERWRITE)) {
+ if (__cursor_page_pinned(cbt) && F_ISSET(cursor, WT_CURSTD_KEY_INT)) {
WT_ERR(__wt_txn_autocommit_check(session));
/*
@@ -773,6 +893,8 @@ retry:
ret = btree->type == BTREE_ROW ?
__cursor_row_modify(session, cbt, true) :
__cursor_col_modify(session, cbt, true);
+ if (ret == 0)
+ goto done;
/*
* The pinned page goes away if we fail for any reason, make
@@ -780,12 +902,9 @@ retry:
* use the pinned page, but that's an unlikely path.) Re-save
* the cursor state: we may retry but eventually fail.
*/
- if (ret != 0) {
- WT_TRET(__cursor_copy_int_key(cursor));
- WT_CURFILE_OP_PUSH;
- goto err;
- }
- goto done;
+ WT_TRET(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+ goto err;
}
/*
@@ -794,9 +913,9 @@ retry:
* eventually fail.
*/
WT_ERR(__cursor_copy_int_key(cursor));
- WT_CURFILE_OP_PUSH;
+ __cursor_state_save(cursor, &state);
- WT_ERR(__cursor_func_init(cbt, true));
+retry: WT_ERR(__cursor_func_init(cbt, true));
if (btree->type == BTREE_ROW) {
WT_ERR(__cursor_row_search(session, cbt, NULL, false));
@@ -857,14 +976,12 @@ done: /*
*/
if (ret == 0)
F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
- if (ret == 0 && positioned) {
+ if (ret == 0 && positioned)
WT_TRET(__wt_key_return(session, cbt));
- if (ret == 0)
- F_SET(cursor, WT_CURSTD_KEY_INT);
- } else
+ else
WT_TRET(__cursor_reset(cbt));
if (ret != 0)
- WT_CURFILE_OP_POP;
+ __cursor_state_restore(cursor, &state);
return (ret);
}
@@ -877,6 +994,7 @@ int
__wt_btcur_update(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
@@ -889,6 +1007,8 @@ __wt_btcur_update(WT_CURSOR_BTREE *cbt)
WT_STAT_DATA_INCR(session, cursor_update);
WT_STAT_DATA_INCRV(session, cursor_update_bytes, cursor->value.size);
+ __cursor_state_save(cursor, &state);
+
if (btree->type == BTREE_ROW)
WT_RET(__cursor_size_chk(session, &cursor->key));
WT_RET(__cursor_size_chk(session, &cursor->value));
@@ -896,7 +1016,48 @@ __wt_btcur_update(WT_CURSOR_BTREE *cbt)
/* It's no longer possible to bulk-load into the tree. */
__cursor_disable_bulk(session, btree);
-retry: WT_RET(__cursor_func_init(cbt, true));
+ /*
+ * If update positioned to an on-page key, the update doesn't require
+ * another search. We don't care about the "overwrite" configuration
+ * because regardless of the overwrite setting, any existing record is
+ * updated, and the record must exist with a positioned cursor. The
+ * cursor won't be positioned on a page with an external key set, but
+ * be sure.
+ */
+ if (__cursor_page_pinned(cbt) && F_ISSET(cursor, WT_CURSTD_KEY_INT)) {
+ WT_ERR(__wt_txn_autocommit_check(session));
+ /*
+ * The cursor position may not be exact (the cursor's comparison
+ * value not equal to zero). Correct to an exact match so we can
+ * update whatever we're pointing at.
+ */
+ cbt->compare = 0;
+ ret = btree->type == BTREE_ROW ?
+ __cursor_row_modify(session, cbt, false) :
+ __cursor_col_modify(session, cbt, false);
+ if (ret == 0)
+ goto done;
+
+ /*
+ * The pinned page goes away if we fail for any reason, make
+ * sure there's a local copy of any key. (Restart could still
+ * use the pinned page, but that's an unlikely path.) Re-save
+ * the cursor state: we may retry but eventually fail.
+ */
+ WT_TRET(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+ goto err;
+ }
+
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key. Re-save the cursor state: we may retry but
+ * eventually fail.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+
+retry: WT_ERR(__cursor_func_init(cbt, true));
if (btree->type == BTREE_ROW) {
WT_ERR(__cursor_row_search(session, cbt, NULL, true));
@@ -945,11 +1106,14 @@ err: if (ret == WT_RESTART) {
* To make this work, we add a field to the btree cursor to pass back a
* pointer to the modify function's allocated update structure.
*/
- if (ret == 0)
+done: if (ret == 0)
WT_TRET(__wt_kv_return(session, cbt, cbt->modify_update));
- if (ret != 0)
+ if (ret != 0) {
WT_TRET(__cursor_reset(cbt));
+ __cursor_state_restore(cursor, &state);
+ }
+
return (ret);
}
@@ -1097,14 +1261,6 @@ __cursor_truncate(WT_SESSION_IMPL *session,
* and we can proceed without concern.
*/
retry: WT_RET(__wt_btcur_search(start));
-
- /*
- * XXX KEITH
- * When the btree cursor code sets/clears the cursor flags (rather than
- * the cursor layer), the set/clear goes away, only the assert remains.
- */
- F_CLR((WT_CURSOR *)start, WT_CURSTD_KEY_SET);
- F_SET((WT_CURSOR *)start, WT_CURSTD_KEY_INT);
WT_ASSERT(session,
F_MASK((WT_CURSOR *)start, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT);
@@ -1161,14 +1317,6 @@ __cursor_truncate_fix(WT_SESSION_IMPL *session,
* refresh the page's modification information.
*/
retry: WT_RET(__wt_btcur_search(start));
-
- /*
- * XXX KEITH
- * When the btree cursor code sets/clears the cursor flags (rather than
- * the cursor layer), the set/clear goes away, only the assert remains.
- */
- F_CLR((WT_CURSOR *)start, WT_CURSTD_KEY_SET);
- F_SET((WT_CURSOR *)start, WT_CURSTD_KEY_INT);
WT_ASSERT(session,
F_MASK((WT_CURSOR *)start, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT);
diff --git a/src/btree/bt_random.c b/src/btree/bt_random.c
index 25ede0a09ac..c5948ec4ab5 100644
--- a/src/btree/bt_random.c
+++ b/src/btree/bt_random.c
@@ -292,14 +292,16 @@ int
__wt_btcur_next_random(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
+ WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
WT_UPDATE *upd;
wt_off_t size;
uint64_t n, skip;
- session = (WT_SESSION_IMPL *)cbt->iface.session;
btree = cbt->btree;
+ cursor = &cbt->iface;
+ session = (WT_SESSION_IMPL *)cbt->iface.session;
/*
* Only supports row-store: applications can trivially select a random
@@ -312,6 +314,8 @@ __wt_btcur_next_random(WT_CURSOR_BTREE *cbt)
WT_STAT_CONN_INCR(session, cursor_next);
WT_STAT_DATA_INCR(session, cursor_next);
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+
#ifdef HAVE_DIAGNOSTIC
/*
* Under some conditions we end up using the underlying cursor.next to
@@ -320,7 +324,6 @@ __wt_btcur_next_random(WT_CURSOR_BTREE *cbt)
*/
__wt_cursor_key_order_reset(cbt);
#endif
-
/*
* If we don't have a current position in the tree, or if retrieving
* random values without sampling, pick a roughly random leaf page in
diff --git a/src/btree/bt_ret.c b/src/btree/bt_ret.c
index 9fc457e2297..f17fa1b85d1 100644
--- a/src/btree/bt_ret.c
+++ b/src/btree/bt_ret.c
@@ -142,8 +142,20 @@ __value_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, WT_UPDATE *upd)
int
__wt_key_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt)
{
- WT_RET(__key_return(session, cbt));
+ WT_CURSOR *cursor;
+
+ cursor = &cbt->iface;
+ /*
+ * We may already have an internal key, in which case the cursor may
+ * not be set up to get another copy (for example, when we rely on a
+ * search-function result).
+ */
+ F_CLR(cursor, WT_CURSTD_KEY_EXT);
+ if (!F_ISSET(cursor, WT_CURSTD_KEY_INT)) {
+ WT_RET(__key_return(session, cbt));
+ F_SET(cursor, WT_CURSTD_KEY_INT);
+ }
return (0);
}
@@ -154,8 +166,15 @@ __wt_key_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt)
int
__wt_kv_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, WT_UPDATE *upd)
{
+ WT_CURSOR *cursor;
+
+ cursor = &cbt->iface;
+
WT_RET(__wt_key_return(session, cbt));
+
+ F_CLR(cursor, WT_CURSTD_VALUE_EXT);
WT_RET(__value_return(session, cbt, upd));
+ F_SET(cursor, WT_CURSTD_VALUE_INT);
return (0);
}
diff --git a/src/cursor/cur_file.c b/src/cursor/cur_file.c
index 274dc1e8f62..205afb607c3 100644
--- a/src/cursor/cur_file.c
+++ b/src/cursor/cur_file.c
@@ -9,29 +9,6 @@
#include "wt_internal.h"
/*
- * WT_BTREE_CURSOR_SAVE_AND_RESTORE
- * Save the cursor's key/value data/size fields, call an underlying btree
- * function, and then consistently handle failure and success.
- */
-#define WT_BTREE_CURSOR_SAVE_AND_RESTORE(cursor, f, ret) do { \
- WT_ITEM __key_copy = (cursor)->key; \
- uint64_t __recno = (cursor)->recno; \
- WT_ITEM __value_copy = (cursor)->value; \
- if (((ret) = (f)) == 0) { \
- F_CLR(cursor, WT_CURSTD_KEY_EXT | WT_CURSTD_VALUE_EXT); \
- F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT); \
- } else { \
- if (F_ISSET(cursor, WT_CURSTD_KEY_EXT)) { \
- (cursor)->recno = __recno; \
- WT_ITEM_SET((cursor)->key, __key_copy); \
- } \
- if (F_ISSET(cursor, WT_CURSTD_VALUE_EXT)) \
- WT_ITEM_SET((cursor)->value, __value_copy); \
- F_CLR(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT); \
- } \
-} while (0)
-
-/*
* __curfile_compare --
* WT_CURSOR->compare method for the btree cursor type.
*/
@@ -109,9 +86,12 @@ __curfile_next(WT_CURSOR *cursor)
cbt = (WT_CURSOR_BTREE *)cursor;
CURSOR_API_CALL(cursor, session, next, cbt->btree);
- F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
- if ((ret = __wt_btcur_next(cbt, false)) == 0)
- F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
+ WT_ERR(__wt_btcur_next(cbt, false));
+
+ /* Next maintains a position, key and value. */
+ WT_ASSERT(session,
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT &&
+ F_MASK(cursor, WT_CURSTD_VALUE_SET) == WT_CURSTD_VALUE_INT);
err: API_END_RET(session, ret);
}
@@ -131,9 +111,12 @@ __wt_curfile_next_random(WT_CURSOR *cursor)
cbt = (WT_CURSOR_BTREE *)cursor;
CURSOR_API_CALL(cursor, session, next, cbt->btree);
- F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
- if ((ret = __wt_btcur_next_random(cbt)) == 0)
- F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
+ WT_ERR(__wt_btcur_next_random(cbt));
+
+ /* Next-random maintains a position, key and value. */
+ WT_ASSERT(session,
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT &&
+ F_MASK(cursor, WT_CURSTD_VALUE_SET) == WT_CURSTD_VALUE_INT);
err: API_END_RET(session, ret);
}
@@ -152,9 +135,12 @@ __curfile_prev(WT_CURSOR *cursor)
cbt = (WT_CURSOR_BTREE *)cursor;
CURSOR_API_CALL(cursor, session, prev, cbt->btree);
- F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
- if ((ret = __wt_btcur_prev(cbt, false)) == 0)
- F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
+ WT_ERR(__wt_btcur_prev(cbt, false));
+
+ /* Prev maintains a position, key and value. */
+ WT_ASSERT(session,
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT &&
+ F_MASK(cursor, WT_CURSTD_VALUE_SET) == WT_CURSTD_VALUE_INT);
err: API_END_RET(session, ret);
}
@@ -175,7 +161,10 @@ __curfile_reset(WT_CURSOR *cursor)
ret = __wt_btcur_reset(cbt);
- F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ /* Reset maintains no position, key or value. */
+ WT_ASSERT(session,
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == 0 &&
+ F_MASK(cursor, WT_CURSTD_VALUE_SET) == 0);
err: API_END_RET(session, ret);
}
@@ -194,10 +183,15 @@ __curfile_search(WT_CURSOR *cursor)
cbt = (WT_CURSOR_BTREE *)cursor;
CURSOR_API_CALL(cursor, session, search, cbt->btree);
- WT_CURSOR_NEEDKEY(cursor);
+ WT_CURSOR_CHECKKEY(cursor);
WT_CURSOR_NOVALUE(cursor);
- WT_BTREE_CURSOR_SAVE_AND_RESTORE(cursor, __wt_btcur_search(cbt), ret);
+ WT_ERR(__wt_btcur_search(cbt));
+
+ /* Search maintains a position, key and value. */
+ WT_ASSERT(session,
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT &&
+ F_MASK(cursor, WT_CURSTD_VALUE_SET) == WT_CURSTD_VALUE_INT);
err: API_END_RET(session, ret);
}
@@ -216,11 +210,15 @@ __curfile_search_near(WT_CURSOR *cursor, int *exact)
cbt = (WT_CURSOR_BTREE *)cursor;
CURSOR_API_CALL(cursor, session, search_near, cbt->btree);
- WT_CURSOR_NEEDKEY(cursor);
+ WT_CURSOR_CHECKKEY(cursor);
WT_CURSOR_NOVALUE(cursor);
- WT_BTREE_CURSOR_SAVE_AND_RESTORE(
- cursor, __wt_btcur_search_near(cbt, exact), ret);
+ WT_ERR(__wt_btcur_search_near(cbt, exact));
+
+ /* Search-near maintains a position, key and value. */
+ WT_ASSERT(session,
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT &&
+ F_MASK(cursor, WT_CURSTD_VALUE_SET) == WT_CURSTD_VALUE_INT);
err: API_END_RET(session, ret);
}
@@ -238,38 +236,33 @@ __curfile_insert(WT_CURSOR *cursor)
cbt = (WT_CURSOR_BTREE *)cursor;
CURSOR_UPDATE_API_CALL(cursor, session, insert, cbt->btree);
+
if (!F_ISSET(cursor, WT_CURSTD_APPEND))
- WT_CURSOR_NEEDKEY(cursor);
- WT_CURSOR_NEEDVALUE(cursor);
+ WT_CURSOR_CHECKKEY(cursor);
+ WT_CURSOR_CHECKVALUE(cursor);
- WT_BTREE_CURSOR_SAVE_AND_RESTORE(cursor, __wt_btcur_insert(cbt), ret);
+ WT_ERR(__wt_btcur_insert(cbt));
/*
- * Insert is the one cursor operation that doesn't end with the cursor
- * pointing to an on-page item (except for column-store appends, where
- * we are returning a key). That is, the application's cursor continues
- * to reference the application's memory after a successful cursor call,
- * which isn't true anywhere else. We don't want to have to explain that
- * scoping corner case, so we reset the application's cursor so it can
- * free the referenced memory and continue on without risking subsequent
- * core dumps.
+ * Insert maintains no position, key or value (except for column-store
+ * appends, where we are returning a key).
*/
- if (ret == 0) {
- if (!F_ISSET(cursor, WT_CURSTD_APPEND))
- F_CLR(cursor, WT_CURSTD_KEY_INT);
- F_CLR(cursor, WT_CURSTD_VALUE_INT);
- }
+ WT_ASSERT(session,
+ (F_ISSET(cursor, WT_CURSTD_APPEND) &&
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT) ||
+ (!F_ISSET(cursor, WT_CURSTD_APPEND) &&
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == 0));
err: CURSOR_UPDATE_API_END(session, ret);
return (ret);
}
/*
- * __curfile_update --
- * WT_CURSOR->update method for the btree cursor type.
+ * __wt_curfile_insert_check --
+ * WT_CURSOR->insert_check method for the btree cursor type.
*/
-static int
-__curfile_update(WT_CURSOR *cursor)
+int
+__wt_curfile_insert_check(WT_CURSOR *cursor)
{
WT_CURSOR_BTREE *cbt;
WT_DECL_RET;
@@ -278,21 +271,21 @@ __curfile_update(WT_CURSOR *cursor)
cbt = (WT_CURSOR_BTREE *)cursor;
CURSOR_UPDATE_API_CALL(cursor, session, update, cbt->btree);
- WT_CURSOR_NEEDKEY(cursor);
- WT_CURSOR_NEEDVALUE(cursor);
+ WT_CURSOR_CHECKKEY(cursor);
+ WT_CURSOR_NOVALUE(cursor);
- WT_BTREE_CURSOR_SAVE_AND_RESTORE(cursor, __wt_btcur_update(cbt), ret);
+ ret = __wt_btcur_insert_check(cbt);
err: CURSOR_UPDATE_API_END(session, ret);
return (ret);
}
/*
- * __wt_curfile_update_check --
- * WT_CURSOR->update_check method for the btree cursor type.
+ * __curfile_update --
+ * WT_CURSOR->update method for the btree cursor type.
*/
-int
-__wt_curfile_update_check(WT_CURSOR *cursor)
+static int
+__curfile_update(WT_CURSOR *cursor)
{
WT_CURSOR_BTREE *cbt;
WT_DECL_RET;
@@ -301,11 +294,15 @@ __wt_curfile_update_check(WT_CURSOR *cursor)
cbt = (WT_CURSOR_BTREE *)cursor;
CURSOR_UPDATE_API_CALL(cursor, session, update, cbt->btree);
- WT_CURSOR_NEEDKEY(cursor);
- WT_CURSOR_NOVALUE(cursor);
+ WT_CURSOR_CHECKKEY(cursor);
+ WT_CURSOR_CHECKVALUE(cursor);
- WT_BTREE_CURSOR_SAVE_AND_RESTORE(
- cursor, __wt_btcur_update_check(cbt), ret);
+ WT_ERR(__wt_btcur_update(cbt));
+
+ /* Update maintains a position, key and value. */
+ WT_ASSERT(session,
+ F_MASK(cursor, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT &&
+ F_MASK(cursor, WT_CURSTD_VALUE_SET) == WT_CURSTD_VALUE_INT);
err: CURSOR_UPDATE_API_END(session, ret);
return (ret);
diff --git a/src/cursor/cur_join.c b/src/cursor/cur_join.c
index 8df8e201173..6135132601b 100644
--- a/src/cursor/cur_join.c
+++ b/src/cursor/cur_join.c
@@ -974,8 +974,8 @@ __curjoin_init_next(WT_SESSION_IMPL *session, WT_CURSOR_JOIN *cjoin,
if (!iterable && F_ISSET(je, WT_CURJOIN_ENTRY_BLOOM)) {
if (session->txn.isolation == WT_ISO_READ_UNCOMMITTED)
WT_ERR_MSG(session, EINVAL,
- "join cursors with Bloom filters cannot be "
- "used with read-uncommitted isolation");
+ "join cursors with Bloom filters cannot be "
+ "used with read-uncommitted isolation");
if (je->bloom == NULL) {
/*
* Look for compatible filters to be shared,
diff --git a/src/evict/evict_lru.c b/src/evict/evict_lru.c
index 84c9990832d..a957d245958 100644
--- a/src/evict/evict_lru.c
+++ b/src/evict/evict_lru.c
@@ -987,7 +987,7 @@ __evict_tune_workers(WT_SESSION_IMPL *session)
if (conn->evict_tune_num_points >= conn->evict_tune_datapts_needed) {
if ((conn->evict_tune_workers_best ==
conn->evict_threads.current_threads) &&
- (conn->evict_threads.current_threads <
+ (conn->evict_threads.current_threads <
conn->evict_threads_max)) {
/*
* Keep adding workers. We will check again
@@ -996,7 +996,7 @@ __evict_tune_workers(WT_SESSION_IMPL *session)
conn->evict_tune_datapts_needed +=
WT_MIN(EVICT_TUNE_DATAPT_MIN,
(conn->evict_threads_max
- - conn->evict_threads.current_threads)/
+ - conn->evict_threads.current_threads) /
EVICT_TUNE_BATCH);
} else {
/*
diff --git a/src/include/cursor.i b/src/include/cursor.i
index 9cb9f5e7189..12044e0e228 100644
--- a/src/include/cursor.i
+++ b/src/include/cursor.i
@@ -76,23 +76,6 @@ __cursor_leave(WT_SESSION_IMPL *session)
}
/*
- * __curfile_enter --
- * Activate a file cursor.
- */
-static inline int
-__curfile_enter(WT_CURSOR_BTREE *cbt)
-{
- WT_SESSION_IMPL *session;
-
- session = (WT_SESSION_IMPL *)cbt->iface.session;
-
- if (!F_ISSET(cbt, WT_CBT_NO_TXN))
- WT_RET(__cursor_enter(session));
- F_SET(cbt, WT_CBT_ACTIVE);
- return (0);
-}
-
-/*
* __cursor_reset --
* Reset the cursor, it no longer holds any position.
*/
@@ -264,8 +247,12 @@ __cursor_func_init(WT_CURSOR_BTREE *cbt, bool reenter)
/* If the transaction is idle, check that the cache isn't full. */
WT_RET(__wt_txn_idle_cache_check(session));
- if (!F_ISSET(cbt, WT_CBT_ACTIVE))
- WT_RET(__curfile_enter(cbt));
+ /* Activate the file cursor. */
+ if (!F_ISSET(cbt, WT_CBT_ACTIVE)) {
+ if (!F_ISSET(cbt, WT_CBT_NO_TXN))
+ WT_RET(__cursor_enter(session));
+ F_SET(cbt, WT_CBT_ACTIVE);
+ }
/*
* If this is an ordinary transactional cursor, make sure we are set up
diff --git a/src/include/extern.h b/src/include/extern.h
index c0aa21b7f4c..a7eb4b491a9 100644
--- a/src/include/extern.h
+++ b/src/include/extern.h
@@ -103,7 +103,7 @@ extern int __wt_btcur_reset(WT_CURSOR_BTREE *cbt) WT_GCC_FUNC_DECL_ATTRIBUTE((wa
extern int __wt_btcur_search(WT_CURSOR_BTREE *cbt) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_btcur_insert(WT_CURSOR_BTREE *cbt) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern int __wt_btcur_update_check(WT_CURSOR_BTREE *cbt) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern int __wt_btcur_insert_check(WT_CURSOR_BTREE *cbt) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_btcur_remove(WT_CURSOR_BTREE *cbt) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_btcur_update(WT_CURSOR_BTREE *cbt) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_btcur_compare(WT_CURSOR_BTREE *a_arg, WT_CURSOR_BTREE *b_arg, int *cmpp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
@@ -289,7 +289,7 @@ extern int __wt_curconfig_open(WT_SESSION_IMPL *session, const char *uri, const
extern int __wt_curds_open( WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *owner, const char *cfg[], WT_DATA_SOURCE *dsrc, WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_curdump_create(WT_CURSOR *child, WT_CURSOR *owner, WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_curfile_next_random(WT_CURSOR *cursor) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
-extern int __wt_curfile_update_check(WT_CURSOR *cursor) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
+extern int __wt_curfile_insert_check(WT_CURSOR *cursor) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_curfile_open(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *owner, const char *cfg[], WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_curindex_open(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *owner, const char *cfg[], WT_CURSOR **cursorp) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
extern int __wt_curjoin_joined(WT_CURSOR *cursor) WT_GCC_FUNC_DECL_ATTRIBUTE((warn_unused_result)) WT_GCC_FUNC_DECL_ATTRIBUTE((visibility("hidden")));
diff --git a/src/include/packing.i b/src/include/packing.i
index d79afe6d4a2..6b4bcd49e04 100644
--- a/src/include/packing.i
+++ b/src/include/packing.i
@@ -198,7 +198,7 @@ next: if (pack->cur == pack->end)
return (0);
default:
WT_RET_MSG(pack->session, EINVAL,
- "Invalid type '%c' found in format '%.*s'",
+ "Invalid type '%c' found in format '%.*s'",
pv->type, (int)(pack->end - pack->orig), pack->orig);
}
diff --git a/src/include/wiredtiger.in b/src/include/wiredtiger.in
index 7223aeae0f6..558e93d3de0 100644
--- a/src/include/wiredtiger.in
+++ b/src/include/wiredtiger.in
@@ -1480,6 +1480,10 @@ struct __wt_session {
* contains.
* @snippet ex_all.c Truncate a range
*
+ * Any specified cursors end with no position, and subsequent calls to
+ * the WT_CURSOR::next (WT_CURSOR::prev) method will iterate from the
+ * beginning (end) of the table.
+ *
* @param session the session handle
* @param name the URI of the file or table to truncate
* @param start optional cursor marking the first record discarded;
diff --git a/src/log/log.c b/src/log/log.c
index 05234619d32..1a27120710b 100644
--- a/src/log/log.c
+++ b/src/log/log.c
@@ -783,8 +783,8 @@ __log_openfile(WT_SESSION_IMPL *session,
__wt_log_desc_byteswap(desc);
if (desc->log_magic != WT_LOG_MAGIC)
WT_PANIC_RET(session, WT_ERROR,
- "log file %s corrupted: Bad magic number %" PRIu32,
- (*fhp)->name, desc->log_magic);
+ "log file %s corrupted: Bad magic number %" PRIu32,
+ (*fhp)->name, desc->log_magic);
if (desc->majorv > WT_LOG_MAJOR_VERSION ||
(desc->majorv == WT_LOG_MAJOR_VERSION &&
desc->minorv > WT_LOG_MINOR_VERSION))
diff --git a/src/lsm/lsm_cursor.c b/src/lsm/lsm_cursor.c
index 3f0b6df8eb0..0de39b38370 100644
--- a/src/lsm/lsm_cursor.c
+++ b/src/lsm/lsm_cursor.c
@@ -178,20 +178,12 @@ __clsm_enter(WT_CURSOR_LSM *clsm, bool reset, bool update)
if (reset) {
WT_ASSERT(session, !F_ISSET(&clsm->iface,
- WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT));
+ WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT));
WT_RET(__clsm_reset_cursors(clsm, NULL));
}
for (;;) {
- /*
- * If the cursor looks up-to-date, check if the cache is full.
- * In case this call blocks, the check will be repeated before
- * proceeding.
- */
- if (clsm->dsk_gen != lsm_tree->dsk_gen &&
- lsm_tree->nchunks != 0)
- goto open;
-
+ /* Check if the cursor looks up-to-date. */
if (clsm->dsk_gen != lsm_tree->dsk_gen &&
lsm_tree->nchunks != 0)
goto open;
@@ -666,7 +658,7 @@ retry: if (F_ISSET(clsm, WT_CLSM_MERGE)) {
*/
if (i != nchunks - 1)
clsm->chunks[i]->cursor->insert =
- __wt_curfile_update_check;
+ __wt_curfile_insert_check;
if (!F_ISSET(clsm, WT_CLSM_MERGE) &&
F_ISSET(chunk, WT_LSM_CHUNK_BLOOM))
@@ -852,8 +844,8 @@ __clsm_compare(WT_CURSOR *a, WT_CURSOR *b, int *cmpp)
WT_ERR_MSG(session, EINVAL,
"comparison method cursors must reference the same object");
- WT_CURSOR_NEEDKEY(a);
- WT_CURSOR_NEEDKEY(b);
+ WT_CURSOR_CHECKKEY(a);
+ WT_CURSOR_CHECKKEY(b);
WT_ERR(__wt_compare(
session, alsm->lsm_tree->collator, &a->key, &b->key, cmpp));
@@ -1529,7 +1521,7 @@ __clsm_insert(WT_CURSOR *cursor)
clsm = (WT_CURSOR_LSM *)cursor;
CURSOR_UPDATE_API_CALL(cursor, session, insert, NULL);
- WT_CURSOR_NEEDKEY(cursor);
+ WT_CURSOR_CHECKKEY(cursor);
WT_CURSOR_NEEDVALUE(cursor);
WT_ERR(__clsm_enter(clsm, false, true));
@@ -1573,7 +1565,7 @@ __clsm_update(WT_CURSOR *cursor)
clsm = (WT_CURSOR_LSM *)cursor;
CURSOR_UPDATE_API_CALL(cursor, session, update, NULL);
- WT_CURSOR_NEEDKEY(cursor);
+ WT_CURSOR_CHECKKEY(cursor);
WT_CURSOR_NEEDVALUE(cursor);
WT_ERR(__clsm_enter(clsm, false, true));
@@ -1620,16 +1612,14 @@ __clsm_remove(WT_CURSOR *cursor)
positioned = F_ISSET(cursor, WT_CURSTD_KEY_INT);
CURSOR_REMOVE_API_CALL(cursor, session, NULL);
- WT_CURSOR_NEEDKEY(cursor);
+ WT_CURSOR_CHECKKEY(cursor);
WT_CURSOR_NOVALUE(cursor);
WT_ERR(__clsm_enter(clsm, false, true));
- if (F_ISSET(cursor, WT_CURSTD_OVERWRITE) ||
- (ret = __clsm_lookup(clsm, &value)) == 0)
- ret = __clsm_put(
- session, clsm, &cursor->key, &__tombstone, positioned);
-
-err: __clsm_leave(clsm);
+ if (!F_ISSET(cursor, WT_CURSTD_OVERWRITE))
+ WT_ERR(__clsm_lookup(clsm, &value));
+ WT_ERR(__clsm_put(
+ session, clsm, &cursor->key, &__tombstone, positioned));
/*
* If the cursor was positioned, it stays positioned with a key but no
@@ -1643,6 +1633,7 @@ err: __clsm_leave(clsm);
else
WT_TRET(cursor->reset(cursor));
+err: __clsm_leave(clsm);
CURSOR_UPDATE_API_END(session, ret);
return (ret);
}
diff --git a/src/lsm/lsm_merge.c b/src/lsm/lsm_merge.c
index a06b736bf0a..8838638f388 100644
--- a/src/lsm/lsm_merge.c
+++ b/src/lsm/lsm_merge.c
@@ -625,7 +625,7 @@ err: if (locked)
else
__wt_verbose(session, WT_VERB_LSM,
"Merge failed with %s",
- __wt_strerror(session, ret, NULL, 0));
+ __wt_strerror(session, ret, NULL, 0));
}
F_CLR(session, WT_SESSION_NO_CACHE | WT_SESSION_NO_EVICTION);
return (ret);
diff --git a/src/lsm/lsm_meta.c b/src/lsm/lsm_meta.c
index 46ead6d6ac4..fc4dde82470 100644
--- a/src/lsm/lsm_meta.c
+++ b/src/lsm/lsm_meta.c
@@ -229,7 +229,7 @@ __lsm_meta_read_v1(
cv.len -= 2;
}
WT_ERR(__wt_config_check(session,
- WT_CONFIG_REF(session, WT_SESSION_create), cv.str, cv.len));
+ WT_CONFIG_REF(session, WT_SESSION_create), cv.str, cv.len));
WT_ERR(__wt_strndup(session, cv.str, cv.len, &lsm_tree->bloom_config));
WT_ERR(__wt_config_getones(
session, lsmconf, "lsm.bloom_hash_count", &cv));
diff --git a/src/lsm/lsm_stat.c b/src/lsm/lsm_stat.c
index 21e8991be94..ed760b6d5f3 100644
--- a/src/lsm/lsm_stat.c
+++ b/src/lsm/lsm_stat.c
@@ -29,8 +29,8 @@ __curstat_lsm_init(
const char *cfg[] = {
WT_CONFIG_BASE(session, WT_SESSION_open_cursor), NULL, NULL };
const char *disk_cfg[] = {
- WT_CONFIG_BASE(session, WT_SESSION_open_cursor),
- "checkpoint=" WT_CHECKPOINT, NULL, NULL };
+ WT_CONFIG_BASE(session, WT_SESSION_open_cursor),
+ "checkpoint=" WT_CHECKPOINT, NULL, NULL };
locked = false;
WT_RET(__wt_lsm_tree_get(session, uri, false, &lsm_tree));
diff --git a/src/schema/schema_create.c b/src/schema/schema_create.c
index 020d5e72c13..a77ca51f9d2 100644
--- a/src/schema/schema_create.c
+++ b/src/schema/schema_create.c
@@ -35,7 +35,7 @@ __wt_direct_io_size_check(WT_SESSION_IMPL *session,
* units of its happy place.
*/
if (FLD_ISSET(conn->direct_io,
- WT_DIRECT_IO_CHECKPOINT | WT_DIRECT_IO_DATA)) {
+ WT_DIRECT_IO_CHECKPOINT | WT_DIRECT_IO_DATA)) {
align = (int64_t)conn->buffer_alignment;
if (align != 0 && (cval.val < align || cval.val % align != 0))
WT_RET_MSG(session, EINVAL,
diff --git a/src/schema/schema_worker.c b/src/schema/schema_worker.c
index e5f71b5d56f..62cdd7d367b 100644
--- a/src/schema/schema_worker.c
+++ b/src/schema/schema_worker.c
@@ -112,10 +112,10 @@ __wt_schema_worker(WT_SESSION_IMPL *session,
wt_session = (WT_SESSION *)session;
if (file_func == __wt_salvage && dsrc->salvage != NULL)
WT_ERR(dsrc->salvage(
- dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg));
+ dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg));
else if (file_func == __wt_verify && dsrc->verify != NULL)
WT_ERR(dsrc->verify(
- dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg));
+ dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg));
else if (file_func == __wt_checkpoint)
;
else if (file_func == __wt_checkpoint_get_handles)
diff --git a/src/session/session_api.c b/src/session/session_api.c
index 3d13287fbe6..51233e5e224 100644
--- a/src/session/session_api.c
+++ b/src/session/session_api.c
@@ -1206,10 +1206,15 @@ __wt_session_range_truncate(WT_SESSION_IMPL *session,
done:
err: /*
- * Close any locally-opened start cursor.
+ * Close any locally-opened start cursor. Reset application cursors,
+ * they've possibly moved and the application cannot use them.
*/
if (local_start)
WT_TRET(start->close(start));
+ else
+ WT_TRET(start->reset(start));
+ if (stop != NULL)
+ WT_TRET(stop->reset(stop));
return (ret);
}
diff --git a/src/session/session_compact.c b/src/session/session_compact.c
index 85214ae6d98..72c072e0fb8 100644
--- a/src/session/session_compact.c
+++ b/src/session/session_compact.c
@@ -210,7 +210,7 @@ __compact_checkpoint(WT_SESSION_IMPL *session)
* work we need to have done is done in the underlying block manager.
*/
const char *checkpoint_cfg[] = {
- WT_CONFIG_BASE(session, WT_SESSION_checkpoint), "force=1", NULL };
+ WT_CONFIG_BASE(session, WT_SESSION_checkpoint), "force=1", NULL };
/* Checkpoints take a lot of time, check if we've run out. */
WT_RET(__wt_session_compact_check_timeout(session));
diff --git a/src/txn/txn.c b/src/txn/txn.c
index e5e59c2b901..6eebf5ecf9f 100644
--- a/src/txn/txn.c
+++ b/src/txn/txn.c
@@ -713,7 +713,7 @@ __wt_txn_stats_update(WT_SESSION_IMPL *session)
snapshot_pinned = txn_global->nsnap_oldest_id;
WT_STAT_SET(session, stats, txn_pinned_range,
- txn_global->current - txn_global->oldest_id);
+ txn_global->current - txn_global->oldest_id);
WT_STAT_SET(session, stats, txn_pinned_snapshot_range,
snapshot_pinned == WT_TXN_NONE ?
diff --git a/src/txn/txn_ckpt.c b/src/txn/txn_ckpt.c
index 6c97922f7e1..5ec8aa19e4c 100644
--- a/src/txn/txn_ckpt.c
+++ b/src/txn/txn_ckpt.c
@@ -306,7 +306,7 @@ __wt_checkpoint_get_handles(WT_SESSION_IMPL *session, const char *cfg[])
WT_ASSERT(session, !F_ISSET(&session->txn, WT_TXN_ERROR));
WT_RET(__wt_metadata_cursor(session, &meta_cursor));
meta_cursor->set_key(meta_cursor, session->dhandle->name);
- ret = __wt_curfile_update_check(meta_cursor);
+ ret = __wt_curfile_insert_check(meta_cursor);
if (ret == WT_ROLLBACK) {
metadata_race = true;
ret = 0;
diff --git a/test/format/config.c b/test/format/config.c
index cd9856d641e..535dcd677e2 100644
--- a/test/format/config.c
+++ b/test/format/config.c
@@ -63,39 +63,42 @@ config_setup(void)
config_in_memory();
/*
- * Choose a data source type and a file type: they're interrelated (LSM
- * trees are only compatible with row-store) and other items depend on
- * them.
+ * Choose a file format and a data source: they're interrelated (LSM is
+ * only compatible with row-store) and other items depend on them.
*/
+ if (!config_is_perm("file_type")) {
+ if (config_is_perm("data_source") && DATASOURCE("lsm"))
+ config_single("file_type=row", 0);
+ else
+ switch (mmrand(NULL, 1, 10)) {
+ case 1: /* 10% */
+ config_single("file_type=fix", 0);
+ break;
+ case 2: case 3: case 4: /* 30% */
+ config_single("file_type=var", 0);
+ break; /* 60% */
+ case 5: case 6: case 7: case 8: case 9: case 10:
+ config_single("file_type=row", 0);
+ break;
+ }
+ }
+ config_map_file_type(g.c_file_type, &g.type);
+
if (!config_is_perm("data_source"))
switch (mmrand(NULL, 1, 3)) {
case 1:
config_single("data_source=file", 0);
break;
case 2:
- if (!g.c_in_memory) {
- config_single("data_source=lsm", 0);
- break;
- }
- /* FALLTHROUGH */
- case 3:
config_single("data_source=table", 0);
break;
- }
-
- if (!config_is_perm("file_type"))
- switch (DATASOURCE("lsm") ? 5 : mmrand(NULL, 1, 10)) {
- case 1:
- config_single("file_type=fix", 0);
- break;
- case 2: case 3: case 4:
- config_single("file_type=var", 0);
- break;
- case 5: case 6: case 7: case 8: case 9: case 10:
- config_single("file_type=row", 0);
+ case 3:
+ if (g.c_in_memory || g.type != ROW)
+ config_single("data_source=table", 0);
+ else
+ config_single("data_source=lsm", 0);
break;
}
- config_map_file_type(g.c_file_type, &g.type);
/*
* If data_source and file_type were both "permanent", we may still
diff --git a/test/format/ops.c b/test/format/ops.c
index 1013d1da30b..05457ebb5a0 100644
--- a/test/format/ops.c
+++ b/test/format/ops.c
@@ -28,14 +28,17 @@
#include "format.h"
-static int col_insert(WT_CURSOR *, WT_ITEM *, WT_ITEM *, uint64_t *);
-static int col_remove(WT_CURSOR *, WT_ITEM *, uint64_t);
-static int col_update(WT_CURSOR *, WT_ITEM *, WT_ITEM *, uint64_t);
+static int col_insert(TINFO *, WT_CURSOR *, WT_ITEM *, WT_ITEM *, uint64_t *);
+static int col_remove(WT_CURSOR *, WT_ITEM *, uint64_t, bool);
+static int col_update(
+ TINFO *, WT_CURSOR *, WT_ITEM *, WT_ITEM *, uint64_t, bool);
static int nextprev(WT_CURSOR *, int);
static void *ops(void *);
-static int row_insert(WT_CURSOR *, WT_ITEM *, WT_ITEM *, uint64_t);
-static int row_remove(WT_CURSOR *, WT_ITEM *, uint64_t);
-static int row_update(WT_CURSOR *, WT_ITEM *, WT_ITEM *, uint64_t);
+static int row_insert(
+ TINFO *, WT_CURSOR *, WT_ITEM *, WT_ITEM *, uint64_t, bool);
+static int row_remove(WT_CURSOR *, WT_ITEM *, uint64_t, bool);
+static int row_update(
+ TINFO *, WT_CURSOR *, WT_ITEM *, WT_ITEM *, uint64_t, bool);
static void table_append_init(void);
#ifdef HAVE_BERKELEY_DB
@@ -243,6 +246,9 @@ typedef struct {
bool insert; /* Insert operation */
} SNAP_OPS;
+#define SNAP_TRACK \
+ (snap != NULL && (size_t)(snap - snap_list) < WT_ELEMENTS(snap_list))
+
/*
* snap_track --
* Add a single snapshot isolation returned value to the list.
@@ -395,15 +401,16 @@ snap_check(WT_CURSOR *cursor,
static void *
ops(void *arg)
{
+ enum { INSERT, READ, REMOVE, UPDATE } op;
SNAP_OPS *snap, snap_list[64];
TINFO *tinfo;
WT_CONNECTION *conn;
- WT_CURSOR *cursor, *cursor_insert;
+ WT_CURSOR *cursor;
WT_DECL_RET;
WT_ITEM *key, _key, *value, _value;
WT_SESSION *session;
uint64_t keyno, ckpt_op, reset_op, session_op;
- uint32_t op, rnd;
+ uint32_t rnd;
u_int i;
int dir;
char *ckpt_config, ckpt_name[64];
@@ -429,9 +436,9 @@ ops(void *arg)
val_gen_setup(&tinfo->rnd, value);
/* Set the first operation where we'll create sessions and cursors. */
- session_op = 0;
+ cursor = NULL;
session = NULL;
- cursor = cursor_insert = NULL;
+ session_op = 0;
/* Set the first operation where we'll perform checkpoint operations. */
ckpt_op = g.c_checkpoints ? mmrand(&tinfo->rnd, 100, 10000) : 0;
@@ -485,24 +492,11 @@ ops(void *arg)
readonly = true;
} else {
/*
- * Open two cursors: one for overwriting and one
- * for append (if it's a column-store).
- *
- * The reason is when testing with existing
- * records, we don't track if a record was
- * deleted or not, which means we must use
- * cursor->insert with overwriting configured.
- * But, in column-store files where we're
- * testing with new, appended records, we don't
- * want to have to specify the record number,
- * which requires an append configuration.
+ * Configure "append", in the case of column
+ * stores, we append when inserting new rows.
*/
- testutil_check(session->open_cursor(session,
- g.uri, NULL, "overwrite", &cursor));
- if (g.type == FIX || g.type == VAR)
- testutil_check(session->open_cursor(
- session, g.uri,
- NULL, "append", &cursor_insert));
+ testutil_check(session->open_cursor(
+ session, g.uri, NULL, "append", &cursor));
/* Pick the next session/cursor close/open. */
session_op += mmrand(&tinfo->rnd, 100, 5000);
@@ -600,111 +594,174 @@ skip_checkpoint: /* Pick the next checkpoint operation. */
intxn = true;
}
+ /* Select a row. */
keyno = mmrand(&tinfo->rnd, 1, (u_int)g.rows);
positioned = false;
+ /* Select an operation. */
+ op = READ;
+ if (!readonly) {
+ i = mmrand(&tinfo->rnd, 1, 100);
+ if (i < g.c_delete_pct)
+ op = REMOVE;
+ else if (i < g.c_delete_pct + g.c_insert_pct)
+ op = INSERT;
+ else if (i <
+ g.c_delete_pct + g.c_insert_pct + g.c_write_pct)
+ op = UPDATE;
+ else
+ op = READ;
+ }
+
/*
- * Perform some number of operations: the percentage of deletes,
- * inserts and writes are specified, reads are the rest. The
- * percentages don't have to add up to 100, a high percentage
- * of deletes will mean fewer inserts and writes. Modifications
- * are always followed by a read to confirm it worked.
+ * Inserts, removes and updates can be done following a cursor
+ * set-key, or based on a cursor position taken from a previous
+ * search. If not already doing a read, position the cursor at
+ * an existing point in the tree 20% of the time.
*/
- op = readonly ? UINT32_MAX : mmrand(&tinfo->rnd, 1, 100);
- if (op < g.c_delete_pct) {
- ++tinfo->remove;
+ positioned = false;
+ if (op != READ && mmrand(&tinfo->rnd, 1, 5) == 1) {
+ ++tinfo->search;
+ ret = read_row(cursor, key, value, keyno);
+ if (ret == 0) {
+ positioned = true;
+ if (SNAP_TRACK)
+ snap_track(snap++, keyno, NULL, value);
+ } else {
+ positioned = false;
+ if (ret == WT_ROLLBACK && intxn)
+ goto deadlock;
+ testutil_assert(ret == WT_NOTFOUND);
+ }
+ }
+#if 0
+ /* Optionally reserve a row. */
+ if (!readonly && intxn && mmrand(&tinfo->rnd, 0, 20) == 1) {
switch (g.type) {
case ROW:
- ret = row_remove(cursor, key, keyno);
+ ret =
+ row_reserve(cursor, key, keyno, positioned);
break;
case FIX:
case VAR:
- ret = col_remove(cursor, key, keyno);
+ ret = col_reserve(cursor, keyno, positioned);
break;
}
if (ret == 0) {
positioned = true;
- if (snap != NULL && (size_t)
- (snap - snap_list) < WT_ELEMENTS(snap_list))
+ __wt_yield();
+ } else {
+ positioned = false;
+ if (ret == WT_ROLLBACK && intxn)
+ goto deadlock;
+ testutil_assert(ret == WT_NOTFOUND);
+ }
+ }
+#endif
+ /* Perform the operation. */
+ switch (op) {
+ case REMOVE:
+ switch (g.type) {
+ case ROW:
+ ret =
+ row_remove(cursor, key, keyno, positioned);
+ break;
+ case FIX:
+ case VAR:
+ ret =
+ col_remove(cursor, key, keyno, positioned);
+ break;
+ }
+ if (ret == 0) {
+ ++tinfo->remove;
+ /*
+ * Don't set positioned: it's unchanged from the
+ * previous state, but not necessarily set.
+ */
+ if (SNAP_TRACK)
snap_track(snap++, keyno, NULL, NULL);
} else {
positioned = false;
if (ret == WT_ROLLBACK && intxn)
goto deadlock;
+ testutil_assert(ret == WT_NOTFOUND);
}
- } else if (op < g.c_delete_pct + g.c_insert_pct) {
- ++tinfo->insert;
+ break;
+ case INSERT:
switch (g.type) {
case ROW:
- key_gen_insert(&tinfo->rnd, key, keyno);
- val_gen(&tinfo->rnd, value, keyno);
- ret = row_insert(cursor, key, value, keyno);
+ ret = row_insert(tinfo,
+ cursor, key, value, keyno, positioned);
break;
case FIX:
case VAR:
/*
- * We can only append so many new records, if
- * we've reached that limit, update a record
- * instead of doing an insert.
+ * We can only append so many new records, once
+ * we reach that limit, update a record instead
+ * of inserting.
*/
if (g.append_cnt >= g.append_max)
- goto skip_insert;
+ goto update_instead_of_insert;
- /* Insert, then reset the insert cursor. */
- val_gen(&tinfo->rnd, value, g.rows + 1);
ret = col_insert(
- cursor_insert, key, value, &keyno);
- testutil_check(
- cursor_insert->reset(cursor_insert));
+ tinfo, cursor, key, value, &keyno);
break;
}
+
+ /* Insert never leaves the cursor positioned. */
positioned = false;
if (ret == 0) {
- if (snap != NULL && (size_t)
- (snap - snap_list) < WT_ELEMENTS(snap_list))
+ ++tinfo->insert;
+ if (SNAP_TRACK)
snap_track(snap++, keyno,
g.type == ROW ? key : NULL, value);
- } else
+ } else {
if (ret == WT_ROLLBACK && intxn)
goto deadlock;
- } else if (
- op < g.c_delete_pct + g.c_insert_pct + g.c_write_pct) {
+ testutil_assert(ret == 0);
+ }
+ break;
+ case UPDATE:
+update_instead_of_insert:
++tinfo->update;
+
+ /* Update the row. */
switch (g.type) {
case ROW:
- key_gen(key, keyno);
- val_gen(&tinfo->rnd, value, keyno);
- ret = row_update(cursor, key, value, keyno);
+ ret = row_update(tinfo,
+ cursor, key, value, keyno, positioned);
break;
case FIX:
case VAR:
-skip_insert: val_gen(&tinfo->rnd, value, keyno);
- ret = col_update(cursor, key, value, keyno);
+ ret = col_update(tinfo,
+ cursor, key, value, keyno, positioned);
break;
}
if (ret == 0) {
positioned = true;
- if (snap != NULL && (size_t)
- (snap - snap_list) < WT_ELEMENTS(snap_list))
+ if (SNAP_TRACK)
snap_track(snap++, keyno, NULL, value);
} else {
positioned = false;
if (ret == WT_ROLLBACK && intxn)
goto deadlock;
+ testutil_assert(ret == 0);
}
- } else {
+ break;
+ case READ:
++tinfo->search;
ret = read_row(cursor, key, value, keyno);
if (ret == 0) {
positioned = true;
- if (snap != NULL && (size_t)
- (snap - snap_list) < WT_ELEMENTS(snap_list))
+ if (SNAP_TRACK)
snap_track(snap++, keyno, NULL, value);
} else {
positioned = false;
if (ret == WT_ROLLBACK && intxn)
goto deadlock;
+ testutil_assert(ret == WT_NOTFOUND);
}
+ break;
}
/*
@@ -727,8 +784,8 @@ skip_insert: val_gen(&tinfo->rnd, value, keyno);
testutil_check(cursor->reset(cursor));
/*
- * If we're in a transaction, commit 40% of the time and
- * rollback 10% of the time.
+ * Continue if not in a transaction, else add more operations
+ * to the transaction half the time.
*/
if (!intxn || (rnd = mmrand(&tinfo->rnd, 1, 10)) > 5)
continue;
@@ -741,6 +798,10 @@ skip_insert: val_gen(&tinfo->rnd, value, keyno);
cursor, snap_list, snap, key, value)) == WT_ROLLBACK)
goto deadlock;
+ /*
+ * If we're in a transaction, commit 40% of the time and
+ * rollback 10% of the time.
+ */
switch (rnd) {
case 1: case 2: case 3: case 4: /* 40% */
testutil_check(
@@ -1040,27 +1101,94 @@ nextprev(WT_CURSOR *cursor, int next)
return (ret);
}
+#if 0
+/*
+ * row_reserve --
+ * Reserve a row in a row-store file.
+ */
+static int
+row_reserve(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno, bool positioned)
+{
+ WT_DECL_RET;
+
+ if (!positioned) {
+ key_gen(key, keyno);
+ cursor->set_key(cursor, key);
+ }
+
+ if (g.logging == LOG_OPS)
+ (void)g.wt_api->msg_printf(g.wt_api, cursor->session,
+ "%-10s{%.*s}", "reserve", (int)key->size, key->data);
+
+ switch (ret = cursor->reserve(cursor)) {
+ case 0:
+ break;
+ case WT_CACHE_FULL:
+ case WT_ROLLBACK:
+ return (WT_ROLLBACK);
+ case WT_NOTFOUND:
+ return (WT_NOTFOUND);
+ default:
+ testutil_die(ret,
+ "row_reserve: reserve row %" PRIu64 " by key", keyno);
+ }
+ return (0);
+}
+
+/*
+ * col_reserve --
+ * Reserve a row in a column-store file.
+ */
+static int
+col_reserve(WT_CURSOR *cursor, uint64_t keyno, bool positioned)
+{
+ WT_DECL_RET;
+
+ if (!positioned)
+ cursor->set_key(cursor, keyno);
+
+ if (g.logging == LOG_OPS)
+ (void)g.wt_api->msg_printf(g.wt_api, cursor->session,
+ "%-10s%" PRIu64, "reserve", keyno);
+
+ switch (ret = cursor->reserve(cursor)) {
+ case 0:
+ break;
+ case WT_CACHE_FULL:
+ case WT_ROLLBACK:
+ return (WT_ROLLBACK);
+ case WT_NOTFOUND:
+ return (WT_NOTFOUND);
+ default:
+ testutil_die(ret, "col_reserve: %" PRIu64, keyno);
+ }
+ return (0);
+}
+#endif
+
/*
* row_update --
* Update a row in a row-store file.
*/
static int
-row_update(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t keyno)
+row_update(TINFO *tinfo, WT_CURSOR *cursor,
+ WT_ITEM *key, WT_ITEM *value, uint64_t keyno, bool positioned)
{
WT_DECL_RET;
- WT_SESSION *session;
- session = cursor->session;
+ if (!positioned) {
+ key_gen(key, keyno);
+ cursor->set_key(cursor, key);
+ }
+ val_gen(&tinfo->rnd, value, keyno);
+ cursor->set_value(cursor, value);
- /* Log the operation */
if (g.logging == LOG_OPS)
- (void)g.wt_api->msg_printf(g.wt_api, session,
+ (void)g.wt_api->msg_printf(g.wt_api, cursor->session,
"%-10s{%.*s}, {%.*s}",
"put",
(int)key->size, key->data, (int)value->size, value->data);
- cursor->set_key(cursor, key);
- cursor->set_value(cursor, value);
switch (ret = cursor->update(cursor)) {
case 0:
break;
@@ -1086,32 +1214,32 @@ row_update(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t keyno)
* Update a row in a column-store file.
*/
static int
-col_update(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t keyno)
+col_update(TINFO *tinfo, WT_CURSOR *cursor,
+ WT_ITEM *key, WT_ITEM *value, uint64_t keyno, bool positioned)
{
WT_DECL_RET;
- WT_SESSION *session;
- session = cursor->session;
+ if (!positioned)
+ cursor->set_key(cursor, keyno);
+ val_gen(&tinfo->rnd, value, keyno);
+ if (g.type == FIX)
+ cursor->set_value(cursor, *(uint8_t *)value->data);
+ else
+ cursor->set_value(cursor, value);
- /* Log the operation */
if (g.logging == LOG_OPS) {
if (g.type == FIX)
- (void)g.wt_api->msg_printf(g.wt_api, session,
+ (void)g.wt_api->msg_printf(g.wt_api, cursor->session,
"%-10s%" PRIu64 " {0x%02" PRIx8 "}",
"update", keyno,
((uint8_t *)value->data)[0]);
else
- (void)g.wt_api->msg_printf(g.wt_api, session,
+ (void)g.wt_api->msg_printf(g.wt_api, cursor->session,
"%-10s%" PRIu64 " {%.*s}",
"update", keyno,
(int)value->size, (char *)value->data);
}
- cursor->set_key(cursor, keyno);
- if (g.type == FIX)
- cursor->set_value(cursor, *(uint8_t *)value->data);
- else
- cursor->set_value(cursor, value);
switch (ret = cursor->update(cursor)) {
case 0:
break;
@@ -1238,22 +1366,29 @@ table_append(uint64_t keyno)
* Insert a row in a row-store file.
*/
static int
-row_insert(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t keyno)
+row_insert(TINFO *tinfo, WT_CURSOR *cursor,
+ WT_ITEM *key, WT_ITEM *value, uint64_t keyno, bool positioned)
{
WT_DECL_RET;
- WT_SESSION *session;
- session = cursor->session;
+ /*
+ * If we positioned the cursor already, it's a test of an update using
+ * the insert method. Otherwise, generate a unique key and insert.
+ */
+ if (!positioned) {
+ key_gen_insert(&tinfo->rnd, key, keyno);
+ cursor->set_key(cursor, key);
+ }
+ val_gen(&tinfo->rnd, value, keyno);
+ cursor->set_value(cursor, value);
/* Log the operation */
if (g.logging == LOG_OPS)
- (void)g.wt_api->msg_printf(g.wt_api, session,
+ (void)g.wt_api->msg_printf(g.wt_api, cursor->session,
"%-10s{%.*s}, {%.*s}",
"insert",
(int)key->size, key->data, (int)value->size, value->data);
- cursor->set_key(cursor, key);
- cursor->set_value(cursor, value);
switch (ret = cursor->insert(cursor)) {
case 0:
break;
@@ -1279,14 +1414,13 @@ row_insert(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t keyno)
* Insert an element in a column-store file.
*/
static int
-col_insert(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t *keynop)
+col_insert(TINFO *tinfo,
+ WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t *keynop)
{
WT_DECL_RET;
- WT_SESSION *session;
uint64_t keyno;
- session = cursor->session;
-
+ val_gen(&tinfo->rnd, value, g.rows + 1);
if (g.type == FIX)
cursor->set_value(cursor, *(uint8_t *)value->data);
else
@@ -1307,12 +1441,12 @@ col_insert(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t *keynop)
if (g.logging == LOG_OPS) {
if (g.type == FIX)
- (void)g.wt_api->msg_printf(g.wt_api, session,
+ (void)g.wt_api->msg_printf(g.wt_api, cursor->session,
"%-10s%" PRIu64 " {0x%02" PRIx8 "}",
"insert", keyno,
((uint8_t *)value->data)[0]);
else
- (void)g.wt_api->msg_printf(g.wt_api, session,
+ (void)g.wt_api->msg_printf(g.wt_api, cursor->session,
"%-10s%" PRIu64 " {%.*s}",
"insert", keyno,
(int)value->size, (char *)value->data);
@@ -1335,21 +1469,19 @@ col_insert(WT_CURSOR *cursor, WT_ITEM *key, WT_ITEM *value, uint64_t *keynop)
* Remove an row from a row-store file.
*/
static int
-row_remove(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno)
+row_remove(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno, bool positioned)
{
WT_DECL_RET;
- WT_SESSION *session;
- session = cursor->session;
-
- key_gen(key, keyno);
+ if (!positioned) {
+ key_gen(key, keyno);
+ cursor->set_key(cursor, key);
+ }
- /* Log the operation */
if (g.logging == LOG_OPS)
- (void)g.wt_api->msg_printf(
- g.wt_api, session, "%-10s%" PRIu64, "remove", keyno);
+ (void)g.wt_api->msg_printf(g.wt_api,
+ cursor->session, "%-10s%" PRIu64, "remove", keyno);
- cursor->set_key(cursor, key);
/* We use the cursor in overwrite mode, check for existence. */
if ((ret = cursor->search(cursor)) == 0)
ret = cursor->remove(cursor);
@@ -1385,19 +1517,17 @@ row_remove(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno)
* Remove a row from a column-store file.
*/
static int
-col_remove(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno)
+col_remove(WT_CURSOR *cursor, WT_ITEM *key, uint64_t keyno, bool positioned)
{
WT_DECL_RET;
- WT_SESSION *session;
- session = cursor->session;
+ if (!positioned)
+ cursor->set_key(cursor, keyno);
- /* Log the operation */
if (g.logging == LOG_OPS)
- (void)g.wt_api->msg_printf(
- g.wt_api, session, "%-10s%" PRIu64, "remove", keyno);
+ (void)g.wt_api->msg_printf(g.wt_api,
+ cursor->session, "%-10s%" PRIu64, "remove", keyno);
- cursor->set_key(cursor, keyno);
/* We use the cursor in overwrite mode, check for existence. */
if ((ret = cursor->search(cursor)) == 0)
ret = cursor->remove(cursor);
diff --git a/test/suite/test_truncate01.py b/test/suite/test_truncate01.py
index 7d2b3862568..98b741ba6a4 100644
--- a/test/suite/test_truncate01.py
+++ b/test/suite/test_truncate01.py
@@ -128,6 +128,7 @@ class test_truncate_cursor_order(wttest.WiredTigerTestCase):
msg = '/the start cursor position is after the stop cursor position/'
self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
lambda: self.session.truncate(None, c1, c2, None), msg)
+ c1.set_key(ds.key(10))
c2.set_key(ds.key(20))
self.session.truncate(None, c1, c2, None)