/* * __wt_btcur_iterate_setup -- * Initialize a cursor for iteration, usually based on a search. */ void __wt_btcur_iterate_setup(WT_CURSOR_BTREE *cbt, int next) { WT_PAGE *page; WT_UNUSED(next); /* * We don't currently have to do any setup when we switch between next * and prev calls, but I'm sure we will someday -- I'm leaving support * here for both flags for that reason. */ F_SET(cbt, WT_CBT_ITERATE_NEXT | WT_CBT_ITERATE_PREV); /* * If we don't have a search page, then we're done, we're starting at * the beginning or end of the tree, not as a result of a search. */ if ((page = cbt->page) == NULL) return; if (page->type == WT_PAGE_ROW_LEAF) { /* * For row-store pages, we need a single item that tells us the * part of the page we're walking (otherwise switching from next * to prev and vice-versa is just too complicated), so we map * the WT_ROW and WT_INSERT_HEAD insert array slots into a * single name space: slot 1 is the "smallest key insert list", * slot 2 is WT_ROW[0], slot 3 is WT_INSERT_HEAD[0], and so on. * This means WT_INSERT lists are odd-numbered slots, and WT_ROW * array slots are even-numbered slots. */ cbt->row_iteration_slot = (cbt->slot + 1) * 2; if (cbt->ins_head != NULL) { if (cbt->ins_head == WT_ROW_INSERT_SMALLEST(page)) cbt->row_iteration_slot = 1; else cbt->row_iteration_slot += 1; } } else { /* * For column-store pages, calculate the largest record on the * page. */ cbt->last_standard_recno = __col_last_recno(page); /* If we're traversing the append list, set the reference. */ if (cbt->ins_head != NULL && cbt->ins_head == WT_COL_APPEND(page)) F_SET(cbt, WT_CBT_ITERATE_APPEND); } }
/* * __cursor_fix_prev -- * Move to the previous, fixed-length column-store item. */ static inline int __cursor_fix_prev(WT_CURSOR_BTREE *cbt, int newpage) { WT_BTREE *btree; WT_ITEM *val; WT_SESSION_IMPL *session; WT_UPDATE *upd; session = (WT_SESSION_IMPL *)cbt->iface.session; btree = session->btree; val = &cbt->iface.value; /* Initialize for each new page. */ if (newpage) { cbt->last_standard_recno = __col_last_recno(cbt->page); if (cbt->last_standard_recno == 0) return (WT_NOTFOUND); __cursor_set_recno(cbt, cbt->last_standard_recno); goto new_page; } /* Move to the previous entry and return the item. */ for (;;) { if (cbt->recno == cbt->page->u.col_fix.recno) return (WT_NOTFOUND); __cursor_set_recno(cbt, cbt->recno - 1); new_page: /* Check any insert list for a matching record. */ cbt->ins_head = WT_COL_UPDATE_SINGLE(cbt->page); cbt->ins = __col_insert_search( cbt->ins_head, cbt->ins_stack, cbt->next_stack, cbt->recno); if (cbt->ins != NULL && cbt->recno != WT_INSERT_RECNO(cbt->ins)) cbt->ins = NULL; upd = cbt->ins == NULL ? NULL : __wt_txn_read(session, cbt->ins->upd); if (upd != NULL) { val->data = WT_UPDATE_DATA(upd); val->size = 1; return (0); } cbt->v = __bit_getv_recno(cbt->page, cbt->recno, btree->bitcnt); val->data = &cbt->v; val->size = 1; return (0); } /* NOTREACHED */ }
/* * __cursor_fix_prev -- * Move to the previous, fixed-length column-store item. */ static inline int __cursor_fix_prev(WT_CURSOR_BTREE *cbt, int newpage) { WT_BTREE *btree; WT_INSERT *ins; WT_ITEM *val; WT_SESSION_IMPL *session; WT_UPDATE *upd; uint64_t *recnop; session = (WT_SESSION_IMPL *)cbt->iface.session; btree = session->btree; recnop = &cbt->iface.recno; val = &cbt->iface.value; /* Initialize for each new page. */ if (newpage) { cbt->last_standard_recno = __col_last_recno(cbt->page); if (cbt->last_standard_recno == 0) return (WT_NOTFOUND); cbt->recno = cbt->last_standard_recno; goto new_page; } /* Move to the previous entry and return the item. */ for (;;) { if (cbt->recno == cbt->page->u.col_fix.recno) return (WT_NOTFOUND); --cbt->recno; new_page: *recnop = cbt->recno; /* Check any insert list for a matching record. */ if ((ins = cbt->ins = __col_insert_search_match( WT_COL_UPDATE_SINGLE(cbt->page), cbt->recno)) != NULL && (upd = __wt_txn_read(session, ins->upd)) != NULL) { val->data = WT_UPDATE_DATA(upd); val->size = 1; return (0); } cbt->v = __bit_getv_recno(cbt->page, cbt->recno, btree->bitcnt); val->data = &cbt->v; val->size = 1; return (0); } /* NOTREACHED */ }
/* * __cursor_var_prev -- * Move to the previous, variable-length column-store item. */ static inline int __cursor_var_prev(WT_CURSOR_BTREE *cbt, int newpage) { WT_CELL *cell; WT_CELL_UNPACK unpack; WT_COL *cip; WT_DECL_RET; WT_ITEM *val; WT_SESSION_IMPL *session; WT_UPDATE *upd; session = (WT_SESSION_IMPL *)cbt->iface.session; val = &cbt->iface.value; /* Initialize for each new page. */ if (newpage) { cbt->last_standard_recno = __col_last_recno(cbt->page); if (cbt->last_standard_recno == 0) return (WT_NOTFOUND); __cursor_set_recno(cbt, cbt->last_standard_recno); goto new_page; } /* Move to the previous entry and return the item. */ for (;;) { __cursor_set_recno(cbt, cbt->recno - 1); new_page: if (cbt->recno < cbt->page->u.col_var.recno) return (WT_NOTFOUND); /* Find the matching WT_COL slot. */ if ((cip = __col_var_search(cbt->page, cbt->recno)) == NULL) return (WT_NOTFOUND); cbt->slot = WT_COL_SLOT(cbt->page, cip); /* Check any insert list for a matching record. */ cbt->ins_head = WT_COL_UPDATE_SLOT(cbt->page, cbt->slot); cbt->ins = __col_insert_search_match(cbt->ins_head, cbt->recno); upd = cbt->ins == NULL ? NULL : __wt_txn_read(session, cbt->ins->upd); if (upd != NULL) { if (WT_UPDATE_DELETED_ISSET(upd)) continue; val->data = WT_UPDATE_DATA(upd); val->size = upd->size; return (0); } /* * If we're at the same slot as the last reference and there's * no matching insert list item, re-use the return information * (so encoded items with large repeat counts aren't repeatedly * decoded). Otherwise, unpack the cell and build the return * information. */ if (cbt->cip_saved != cip) { if ((cell = WT_COL_PTR(cbt->page, cip)) == NULL) continue; __wt_cell_unpack(cell, &unpack); if (unpack.type == WT_CELL_DEL) continue; /* * Restart for a variable-length column-store. We could * catch restart higher up the call-stack but there's no * point to it: unlike row-store (where a normal search * path finds cached overflow values), we have to access * the page's reconciliation structures, and that's as * easy here as higher up the stack. */ if ((ret = __wt_cell_unpack_ref( session, &unpack, &cbt->tmp)) == WT_RESTART) ret = __wt_ovfl_cache_col_restart( session, cbt->page, &unpack, &cbt->tmp); WT_RET(ret); cbt->cip_saved = cip; } val->data = cbt->tmp.data; val->size = cbt->tmp.size; return (0); } /* NOTREACHED */ }
/* * __cursor_fix_append_prev -- * Return the previous fixed-length entry on the append list. */ static inline int __cursor_fix_append_prev(WT_CURSOR_BTREE *cbt, int newpage) { WT_ITEM *val; WT_SESSION_IMPL *session; WT_UPDATE *upd; session = (WT_SESSION_IMPL *)cbt->iface.session; val = &cbt->iface.value; if (newpage) { if ((cbt->ins = WT_SKIP_LAST(cbt->ins_head)) == NULL) return (WT_NOTFOUND); } else { /* * Handle the special case of leading implicit records, that is, * there aren't any records in the tree not on the append list, * and the first record on the append list isn't record 1. * * The "right" place to handle this is probably in our caller. * The high-level cursor-previous routine would: * -- call this routine to walk the append list * -- call the routine to walk the standard page items * -- call the tree walk routine looking for a previous page * Each of them returns WT_NOTFOUND, at which point our caller * checks the cursor record number, and if it's larger than 1, * returns the implicit records. Instead, I'm trying to detect * the case here, mostly because I don't want to put that code * into our caller. Anyway, if this code breaks for any reason, * that's the way I'd go. * * If we're not pointing to a WT_INSERT entry, or we can't find * a WT_INSERT record that precedes our record name-space, check * if there are any records on the page. If there aren't, then * we're in the magic zone, keep going until we get to a record * number of 1. */ if (cbt->ins != NULL && cbt->recno <= WT_INSERT_RECNO(cbt->ins)) WT_RET(__cursor_skip_prev(cbt)); if (cbt->ins == NULL && (cbt->recno == 1 || __col_last_recno(cbt->page) != 0)) return (WT_NOTFOUND); } /* * This code looks different from the cursor-next code. The append * list appears on the last page of the tree and contains the last * records in the tree. If we're iterating through the tree, starting * at the last record in the tree, by definition we're starting a new * iteration and we set the record number to the last record found in * the tree. Otherwise, decrement the record. */ if (newpage) __cursor_set_recno(cbt, WT_INSERT_RECNO(cbt->ins)); else __cursor_set_recno(cbt, cbt->recno - 1); /* * Fixed-width column store appends are inherently non-transactional. * Even a non-visible update by a concurrent or aborted transaction * changes the effective end of the data. The effect is subtle because * of the blurring between deleted and empty values, but ideally we * would skip all uncommitted changes at the end of the data. This * doesn't apply to variable-width column stores because the implicitly * created records written by reconciliation are deleted and so can be * never seen by a read. */ if (cbt->ins == NULL || cbt->recno > WT_INSERT_RECNO(cbt->ins) || (upd = __wt_txn_read(session, cbt->ins->upd)) == NULL) { cbt->v = 0; val->data = &cbt->v; } else val->data = WT_UPDATE_DATA(upd); val->size = 1; return (0); }
/* * __wt_col_modify -- * Column-store delete, insert, and update. */ int __wt_col_modify(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, int op) { WT_BTREE *btree; WT_DECL_RET; WT_INSERT *ins, *ins_copy; WT_INSERT_HEAD **inshead, *new_inshead, **new_inslist; WT_ITEM *value, _value; WT_PAGE *page; WT_UPDATE *old_upd, *upd, *upd_obsolete; size_t ins_size, new_inshead_size, new_inslist_size, upd_size; uint64_t recno; u_int skipdepth; int i, logged; btree = cbt->btree; page = cbt->page; recno = cbt->iface.recno; logged = 0; WT_ASSERT(session, op != 1); switch (op) { case 2: /* Remove */ if (btree->type == BTREE_COL_FIX) { value = &_value; value->data = ""; value->size = 1; } else value = NULL; break; case 3: /* Insert/Update */ default: value = &cbt->iface.value; /* * There's some chance the application specified a record past * the last record on the page. If that's the case, and we're * inserting a new WT_INSERT/WT_UPDATE pair, it goes on the * append list, not the update list. */ if (recno == 0 || recno > __col_last_recno(page)) op = 1; break; } /* If we don't yet have a modify structure, we'll need one. */ WT_RET(__wt_page_modify_init(session, page)); ins = NULL; new_inshead = NULL; new_inslist = NULL; upd = NULL; /* * Delete, insert or update a column-store entry. * * If modifying a previously modified record, create a new WT_UPDATE * entry and have a serialized function link it into an existing * WT_INSERT entry's WT_UPDATE list. * * Else, allocate an insert array as necessary, build a WT_INSERT and * WT_UPDATE structure pair, and call a serialized function to insert * the WT_INSERT structure. */ if (cbt->compare == 0 && cbt->ins != NULL) { /* Make sure the update can proceed. */ WT_ERR( __wt_update_check(session, page, old_upd = cbt->ins->upd)); /* Allocate the WT_UPDATE structure and transaction ID. */ WT_ERR(__wt_update_alloc(session, value, &upd, &upd_size)); WT_ERR(__wt_txn_modify(session, &upd->txnid)); logged = 1; /* Serialize the update. */ WT_ERR(__wt_update_serial(session, page, cbt->write_gen, &cbt->ins->upd, old_upd, NULL, 0, &upd, upd_size, &upd_obsolete)); /* Discard any obsolete WT_UPDATE structures. */ if (upd_obsolete != NULL) __wt_update_obsolete_free(session, page, upd_obsolete); } else { /* Make sure the update can proceed. */ WT_ERR(__wt_update_check(session, page, NULL)); /* There may be no insert list, allocate as necessary. */ new_inshead_size = new_inslist_size = 0; if (op == 1) { if (page->modify->append == NULL) { new_inslist_size = 1 * sizeof(WT_INSERT_HEAD *); WT_ERR( __wt_calloc_def(session, 1, &new_inslist)); inshead = &new_inslist[0]; } else inshead = &page->modify->append[0]; cbt->ins_head = *inshead; } else if (page->type == WT_PAGE_COL_FIX) { if (page->modify->update == NULL) { new_inslist_size = 1 * sizeof(WT_INSERT_HEAD *); WT_ERR( __wt_calloc_def(session, 1, &new_inslist)); inshead = &new_inslist[0]; } else inshead = &page->modify->update[0]; } else { if (page->modify->update == NULL) { new_inslist_size = page->entries * sizeof(WT_INSERT_HEAD *); WT_ERR(__wt_calloc_def( session, page->entries, &new_inslist)); inshead = &new_inslist[cbt->slot]; } else inshead = &page->modify->update[cbt->slot]; } /* There may be no WT_INSERT list, allocate as necessary. */ if (*inshead == NULL) { new_inshead_size = sizeof(WT_INSERT_HEAD); WT_ERR(__wt_calloc_def(session, 1, &new_inshead)); for (i = 0; i < WT_SKIP_MAXDEPTH; i++) { cbt->ins_stack[i] = &new_inshead->head[i]; cbt->next_stack[i] = NULL; } cbt->ins_head = new_inshead; } /* Choose a skiplist depth for this insert. */ skipdepth = __wt_skip_choose_depth(); /* * Allocate a WT_INSERT/WT_UPDATE pair and transaction ID, and * update the cursor to reference it. */ WT_ERR(__col_insert_alloc( session, recno, skipdepth, &ins, &ins_size)); WT_ERR(__wt_update_alloc(session, value, &upd, &upd_size)); WT_ERR(__wt_txn_modify(session, &upd->txnid)); logged = 1; ins->upd = upd; ins_size += upd_size; cbt->ins = ins; /* Insert or append the WT_INSERT structure. */ if (op == 1) { /* * The serialized function clears ins: take a copy of * the pointer so we can look up the record number. */ ins_copy = ins; WT_ERR(__wt_col_append_serial(session, page, cbt->write_gen, inshead, cbt->ins_stack, cbt->next_stack, &new_inslist, new_inslist_size, &new_inshead, new_inshead_size, &ins, ins_size, skipdepth)); /* Put the new recno into the cursor. */ cbt->recno = WT_INSERT_RECNO(ins_copy); } else WT_ERR(__wt_insert_serial(session, page, cbt->write_gen, inshead, cbt->ins_stack, cbt->next_stack, &new_inslist, new_inslist_size, &new_inshead, new_inshead_size, &ins, ins_size, skipdepth)); } if (0) { err: /* * Remove the update from the current transaction, so we don't * try to modify it on rollback. */ if (logged) __wt_txn_unmodify(session); __wt_free(session, ins); __wt_free(session, upd); } __wt_free(session, new_inslist); __wt_free(session, new_inshead); return (ret); }
/* * __cursor_var_prev -- * Move to the previous, variable-length column-store item. */ static inline int __cursor_var_prev(WT_CURSOR_BTREE *cbt, int newpage) { WT_CELL *cell; WT_CELL_UNPACK unpack; WT_COL *cip; WT_INSERT *ins; WT_ITEM *val; WT_SESSION_IMPL *session; WT_UPDATE *upd; uint64_t *recnop; session = (WT_SESSION_IMPL *)cbt->iface.session; recnop = &cbt->iface.recno; val = &cbt->iface.value; /* Initialize for each new page. */ if (newpage) { cbt->last_standard_recno = __col_last_recno(cbt->page); if (cbt->last_standard_recno == 0) return (WT_NOTFOUND); cbt->recno = cbt->last_standard_recno; goto new_page; } /* Move to the previous entry and return the item. */ for (;;) { --cbt->recno; new_page: *recnop = cbt->recno; if (cbt->recno < cbt->page->u.col_var.recno) return (WT_NOTFOUND); /* Find the matching WT_COL slot. */ if ((cip = __col_var_search(cbt->page, cbt->recno)) == NULL) return (WT_NOTFOUND); /* Check any insert list for a matching record. */ if ((ins = __col_insert_search_match( WT_COL_UPDATE(cbt->page, cip), cbt->recno)) != NULL && (upd = __wt_txn_read(session, ins->upd)) != NULL) { if (WT_UPDATE_DELETED_ISSET(upd)) continue; cbt->ins = ins; val->data = WT_UPDATE_DATA(upd); val->size = upd->size; return (0); } /* * If we're at the same slot as the last reference and there's * no matching insert list item, re-use the return information * (so encoded items with large repeat counts aren't repeatedly * decoded). Otherwise, unpack the cell and build the return * information. */ if (cbt->cip_saved != cip) { if ((cell = WT_COL_PTR(cbt->page, cip)) == NULL) continue; __wt_cell_unpack(cell, &unpack); switch (unpack.type) { case WT_CELL_DEL: continue; case WT_CELL_VALUE: if (session->btree->huffman_value == NULL) { cbt->tmp.data = unpack.data; cbt->tmp.size = unpack.size; break; } /* FALLTHROUGH */ default: WT_RET(__wt_cell_unpack_copy( session, &unpack, &cbt->tmp)); } cbt->cip_saved = cip; } val->data = cbt->tmp.data; val->size = cbt->tmp.size; return (0); } /* NOTREACHED */ }
/* * __cursor_var_next -- * Move to the next, variable-length column-store item. */ static inline int __cursor_var_next(WT_CURSOR_BTREE *cbt, int newpage) { WT_CELL *cell; WT_CELL_UNPACK unpack; WT_COL *cip; WT_ITEM *val; WT_SESSION_IMPL *session; WT_UPDATE *upd; session = (WT_SESSION_IMPL *)cbt->iface.session; val = &cbt->iface.value; /* Initialize for each new page. */ if (newpage) { cbt->last_standard_recno = __col_last_recno(cbt->page); if (cbt->last_standard_recno == 0) return (WT_NOTFOUND); __cursor_set_recno(cbt, cbt->page->u.col_var.recno); goto new_page; } /* Move to the next entry and return the item. */ for (;;) { if (cbt->recno >= cbt->last_standard_recno) return (WT_NOTFOUND); __cursor_set_recno(cbt, cbt->recno + 1); new_page: /* Find the matching WT_COL slot. */ if ((cip = __col_var_search(cbt->page, cbt->recno)) == NULL) return (WT_NOTFOUND); cbt->slot = WT_COL_SLOT(cbt->page, cip); /* Check any insert list for a matching record. */ cbt->ins_head = WT_COL_UPDATE_SLOT(cbt->page, cbt->slot); cbt->ins = __col_insert_search_match(cbt->ins_head, cbt->recno); upd = cbt->ins == NULL ? NULL : __wt_txn_read(session, cbt->ins->upd); if (upd != NULL) { if (WT_UPDATE_DELETED_ISSET(upd)) continue; val->data = WT_UPDATE_DATA(upd); val->size = upd->size; return (0); } /* * If we're at the same slot as the last reference and there's * no matching insert list item, re-use the return information * (so encoded items with large repeat counts aren't repeatedly * decoded). Otherwise, unpack the cell and build the return * information. */ if (cbt->cip_saved != cip) { if ((cell = WT_COL_PTR(cbt->page, cip)) == NULL) continue; __wt_cell_unpack(cell, &unpack); if (unpack.type == WT_CELL_DEL) continue; WT_RET(__wt_page_cell_data_ref( session, cbt->page, &unpack, &cbt->tmp)); cbt->cip_saved = cip; } val->data = cbt->tmp.data; val->size = cbt->tmp.size; return (0); } /* NOTREACHED */ }