/* * __split_ovfl_key_cleanup -- * Handle cleanup for on-page row-store overflow keys. */ static int __split_ovfl_key_cleanup(WT_SESSION_IMPL *session, WT_PAGE *page, WT_REF *ref) { WT_CELL *cell; WT_CELL_UNPACK kpack; WT_IKEY *ikey; uint32_t cell_offset; /* * A key being discarded (page split) or moved to a different page (page * deepening) may be an on-page overflow key. Clear any reference to an * underlying disk image, and, if the key hasn't been deleted, delete it * along with any backing blocks. */ if ((ikey = __wt_ref_key_instantiated(ref)) == NULL) return (0); if ((cell_offset = ikey->cell_offset) == 0) return (0); /* Leak blocks rather than try this twice. */ ikey->cell_offset = 0; cell = WT_PAGE_REF_OFFSET(page, cell_offset); __wt_cell_unpack(cell, &kpack); if (kpack.ovfl && kpack.raw != WT_CELL_KEY_OVFL_RM) WT_RET(__wt_ovfl_discard(session, cell)); return (0); }
/* * __merge_switch_page -- * Switch a page from the locked tree into the new tree. */ static void __merge_switch_page(WT_PAGE *parent, WT_REF *ref, WT_VISIT_STATE *state) { WT_IKEY *ikey; WT_PAGE *child; WT_PAGE_MODIFY *modify; WT_REF *newref; if (state->split != 0 && state->refcnt++ == state->split) { state->page = state->second; state->ref = state->second_ref; } newref = state->ref++; if (ref->addr != NULL) __merge_transfer_footprint(state->page, parent, sizeof(WT_ADDR) + ((WT_ADDR *)ref->addr)->size); if (parent->type == WT_PAGE_ROW_INT && (ikey = __wt_ref_key_instantiated(ref)) != NULL) __merge_transfer_footprint(state->page, parent, sizeof(WT_IKEY) + ikey->size); if (ref->state == WT_REF_LOCKED) { child = ref->page; /* * If the child has been split, update the split page to point * into the new tree. That way, if the split-merge page is * later swapped into place, it will point to the new parent. * * The order here is important: the parent page should point to * the original child page, so we link that in last. */ if ((modify = child->modify) != NULL && F_ISSET(modify, WT_PM_REC_SPLIT)) WT_LINK_PAGE(state->page, newref, modify->u.split); WT_LINK_PAGE(state->page, newref, child); /* * If we have a child that is a live internal page, its subtree * was locked by __rec_review. We're swapping it into the new * tree, unlock it now. */ if (child->type == WT_PAGE_ROW_INT || child->type == WT_PAGE_COL_INT) __merge_unlock(child); newref->state = WT_REF_MEM; } WT_CLEAR(*ref); }
/* * __wt_free_ref -- * Discard the contents of a WT_REF structure (optionally including the * pages it references). */ void __wt_free_ref( WT_SESSION_IMPL *session, WT_REF *ref, int page_type, bool free_pages) { WT_IKEY *ikey; if (ref == NULL) return; /* * Optionally free the referenced pages. (The path to free referenced * page is used for error cleanup, no instantiated and then discarded * page should have WT_REF entries with real pages. The page may have * been marked dirty as well; page discard checks for that, so we mark * it clean explicitly.) */ if (free_pages && ref->page != NULL) { __wt_page_modify_clear(session, ref->page); __wt_page_out(session, &ref->page); } /* * Optionally free row-store WT_REF key allocation. Historic versions of * this code looked in a passed-in page argument, but that is dangerous, * some of our error-path callers create WT_REF structures without ever * setting WT_REF.home or having a parent page to which the WT_REF will * be linked. Those WT_REF structures invariably have instantiated keys, * (they obviously cannot be on-page keys), and we must free the memory. */ switch (page_type) { case WT_PAGE_ROW_INT: case WT_PAGE_ROW_LEAF: if ((ikey = __wt_ref_key_instantiated(ref)) != NULL) __wt_free(session, ikey); break; } /* * Free any address allocation; if there's no linked WT_REF page, it * must be allocated. */ __wt_ref_addr_free(session, ref); /* Free any page-deleted information. */ if (ref->page_del != NULL) { __wt_free(session, ref->page_del->update_list); __wt_free(session, ref->page_del); } __wt_overwrite_and_free(session, ref); }
/* * __wt_free_ref -- * Discard the contents of a WT_REF structure (optionally including the * pages it references). */ void __wt_free_ref( WT_SESSION_IMPL *session, WT_PAGE *page, WT_REF *ref, bool free_pages) { WT_IKEY *ikey; if (ref == NULL) return; /* * Optionally free the referenced pages. (The path to free referenced * page is used for error cleanup, no instantiated and then discarded * page should have WT_REF entries with real pages. The page may have * been marked dirty as well; page discard checks for that, so we mark * it clean explicitly.) */ if (free_pages && ref->page != NULL) { __wt_page_modify_clear(session, ref->page); __wt_page_out(session, &ref->page); } /* Free any key allocation. */ switch (page->type) { case WT_PAGE_ROW_INT: case WT_PAGE_ROW_LEAF: if ((ikey = __wt_ref_key_instantiated(ref)) != NULL) __wt_free(session, ikey); break; } /* Free any address allocation. */ if (ref->addr != NULL && __wt_off_page(page, ref->addr)) { __wt_free(session, ((WT_ADDR *)ref->addr)->addr); __wt_free(session, ref->addr); } /* Free any page-deleted information. */ if (ref->page_del != NULL) { __wt_free(session, ref->page_del->update_list); __wt_free(session, ref->page_del); } __wt_overwrite_and_free(session, ref); }
/* * __split_ovfl_key_cleanup -- * Handle cleanup for on-page row-store overflow keys. */ static int __split_ovfl_key_cleanup(WT_SESSION_IMPL *session, WT_PAGE *page, WT_REF *ref) { WT_CELL *cell; WT_CELL_UNPACK kpack; WT_IKEY *ikey; uint32_t cell_offset; /* There's a per-page flag if there are any overflow keys at all. */ if (!F_ISSET_ATOMIC(page, WT_PAGE_OVERFLOW_KEYS)) return (0); /* * A key being discarded (page split) or moved to a different page (page * deepening) may be an on-page overflow key. Clear any reference to an * underlying disk image, and, if the key hasn't been deleted, delete it * along with any backing blocks. */ if ((ikey = __wt_ref_key_instantiated(ref)) == NULL) return (0); if ((cell_offset = ikey->cell_offset) == 0) return (0); /* Leak blocks rather than try this twice. */ ikey->cell_offset = 0; cell = WT_PAGE_REF_OFFSET(page, cell_offset); __wt_cell_unpack(cell, &kpack); if (kpack.ovfl && kpack.raw != WT_CELL_KEY_OVFL_RM) { /* * Eviction cannot free overflow items once a checkpoint is * running in a tree: that can corrupt the checkpoint's block * management. Assert that checkpoints aren't running to make * sure we're catching all paths and to avoid regressions. */ WT_ASSERT(session, S2BT(session)->checkpointing != WT_CKPT_RUNNING); WT_RET(__wt_ovfl_discard(session, cell)); } return (0); }
/* * __split_ref_deepen_move -- * Move a WT_REF from a parent to a child in service of a split to deepen * the tree, including updating the accounting information. */ static int __split_ref_deepen_move(WT_SESSION_IMPL *session, WT_PAGE *parent, WT_REF *ref, size_t *parent_decrp, size_t *child_incrp) { WT_ADDR *addr; WT_CELL_UNPACK unpack; WT_DECL_RET; WT_IKEY *ikey; size_t size; void *key; /* * Instantiate row-store keys, and column- and row-store addresses in * the WT_REF structures referenced by a page that's being split (and * deepening the tree). The WT_REF structures aren't moving, but the * index references are moving from the page we're splitting to a set * of child pages, and so we can no longer reference the block image * that remains with the page being split. * * No locking is required to update the WT_REF structure because we're * the only thread splitting the parent page, and there's no way for * readers to race with our updates of single pointers. The changes * have to be written before the page goes away, of course, our caller * owns that problem. * * Row-store keys, first. */ if (parent->type == WT_PAGE_ROW_INT) { if ((ikey = __wt_ref_key_instantiated(ref)) == NULL) { __wt_ref_key(parent, ref, &key, &size); WT_RET(__wt_row_ikey(session, 0, key, size, ref)); ikey = ref->key.ikey; } else { WT_RET(__split_ovfl_key_cleanup(session, parent, ref)); *parent_decrp += sizeof(WT_IKEY) + ikey->size; } *child_incrp += sizeof(WT_IKEY) + ikey->size; } /* * If there's no address (the page has never been written), or the * address has been instantiated, there's no work to do. Otherwise, * get the address from the on-page cell. */ addr = ref->addr; if (addr != NULL && !__wt_off_page(parent, addr)) { __wt_cell_unpack((WT_CELL *)ref->addr, &unpack); WT_RET(__wt_calloc_one(session, &addr)); if ((ret = __wt_strndup( session, unpack.data, unpack.size, &addr->addr)) != 0) { __wt_free(session, addr); return (ret); } addr->size = (uint8_t)unpack.size; addr->type = unpack.raw == WT_CELL_ADDR_INT ? WT_ADDR_INT : WT_ADDR_LEAF; ref->addr = addr; } /* And finally, the WT_REF itself. */ WT_MEM_TRANSFER(*parent_decrp, *child_incrp, sizeof(WT_REF)); return (0); }