int btree_get(btree_t bt, const void *key, void *data) { const struct btree_def *def = bt->def; struct btree_page *p = bt->root; int h; check_btree(bt); if (!key) return btree_select(bt, NULL, BTREE_READ, NULL, data); for (h = bt->root->height; h >= 0; h--) { int s = find_key_le(p, key); if (h) { assert (s >= 0 && s < p->num_children); p = *PAGE_PTR(p, s); } else if (s >= 0 && !def->compare(key, PAGE_KEY(p, s))) { memcpy(data, PAGE_DATA(p, s), def->data_size); return 0; } } return 1; }
void btree_clear(btree_t bt) { struct btree_page *p; struct btree_page *path_up = 0; check_btree(bt); /* The cursor will have nothing to point to after this. */ bt->slot[0] = -1; /* First, find the last leaf node, which we can re-use as an * empty root. */ p = bt->root; while (p->height) { path_up = p; p = *PAGE_PTR(p, p->num_children - 1); } /* Unlink it from the tree and then destroy everything else. */ if (path_up) { path_up->num_children--; destroy_page(bt->root); } /* Clear it out and make it the new root */ p->num_children = 0; bt->root = p; }
int btree_select(btree_t bt, const void *key, btree_selmode_t mode, void *key_ret, void *data_ret) { const struct btree_def *def = bt->def; check_btree(bt); switch (mode) { case BTREE_CLEAR: bt->slot[0] = -1; break; case BTREE_READ: break; case BTREE_EXACT: case BTREE_LE: if (!trace_path(bt, key, bt->path, bt->slot) && mode == BTREE_EXACT) bt->slot[0] = -1; break; case BTREE_FIRST: cursor_first(bt); break; case BTREE_NEXT: cursor_next(bt); break; } /* Return the data at the cursor */ if (bt->slot[0] >= 0) { if (key_ret) memcpy(key_ret, PAGE_KEY(bt->path[0], bt->slot[0]), def->key_size); if (data_ret) memcpy(data_ret, PAGE_DATA(bt->path[0], bt->slot[0]), def->data_size); return 0; } return 1; }
int btree_delete(btree_t bt, const void *key) { const struct btree_def *def = bt->def; const int halfsize = def->branches / 2; struct btree_page *path[MAX_HEIGHT] = {0}; int slot[MAX_HEIGHT] = {0}; int h; check_btree(bt); /* Trace a path to the item to be deleted */ if (!key) { if (bt->slot[0] < 0) return 1; memcpy(path, bt->path, sizeof(path)); memcpy(slot, bt->slot, sizeof(slot)); } else if (!trace_path(bt, key, path, slot)) { return 1; } /* Select the next item if we're deleting at the cursor */ if (bt->slot[0] == slot[0] && bt->path[0] == path[0]) cursor_next(bt); /* Delete from the leaf node. If it's still full enough, then we don't * need to do anything else. */ delete_item(path[0], slot[0]); if (path[0]->num_children >= halfsize) return 0; /* Trace back up the tree, fixing underfull nodes. If we can fix by * borrowing, do it and we're done. Otherwise, we need to fix by * merging, which may result in another underfull node, and we need * to continue. */ for (h = 1; h <= bt->root->height; h++) { struct btree_page *p = path[h]; struct btree_page *c = path[h - 1]; int s = slot[h]; if (s > 0) { /* Borrow/merge from lower page */ struct btree_page *d = *PAGE_PTR(p, s - 1); if (d->num_children > halfsize) { move_item(d, d->num_children - 1, c, 0); memcpy(PAGE_KEY(p, s), PAGE_KEY(c, 0), def->key_size); return 0; } merge_pages(d, c); delete_item(p, s); free(c); } else { /* Borrow/merge from higher page */ struct btree_page *d = *PAGE_PTR(p, s + 1); if (d->num_children > halfsize) { move_item(d, 0, c, c->num_children); memcpy(PAGE_KEY(p, s + 1), PAGE_KEY(d, 0), def->key_size); return 0; } merge_pages(c, d); delete_item(p, s + 1); free(d); } if (p->num_children >= halfsize) return 0; } /* If the root contains only a single pointer to another page, * shrink the tree. This does not affect the cursor. */ if (bt->root->height && bt->root->num_children == 1) { struct btree_page *old = bt->root; bt->root = *PAGE_PTR(old, 0); free(old); } return 0; }
int btree_put(btree_t bt, const void *key, const void *data) { const struct btree_def *def = bt->def; struct btree_page *new_root = NULL; struct btree_page *path_new[MAX_HEIGHT] = {0}; struct btree_page *path_old[MAX_HEIGHT] = {0}; int slot_old[MAX_HEIGHT] = {0}; int h; check_btree(bt); /* Special case: cursor overwrite */ if (!key) { if (bt->slot[0] < 0) { fprintf(stderr, "btree: put at invalid cursor\n"); return -1; } memcpy(PAGE_DATA(bt->path[0], bt->slot[0]), data, def->data_size); return 1; } /* Find a path down the tree that leads to the page which should * contain this datum (though the page might be too big to hold it). */ if (trace_path(bt, key, path_old, slot_old)) { /* Special case: overwrite existing item */ memcpy(PAGE_DATA(path_old[0], slot_old[0]), data, def->data_size); return 1; } /* Trace from the leaf up. If the leaf is at its maximum size, it will * need to split, and cause a pointer to be added in the parent page * of the same node (which may in turn cause it to split). */ for (h = 0; h <= bt->root->height; h++) { if (path_old[h]->num_children < def->branches) break; path_new[h] = allocate_page(bt, h); if (!path_new[h]) goto fail; } /* If the split reaches the top (i.e. the root splits), then we need * to allocate a new root node. */ if (h > bt->root->height) { if (h >= MAX_HEIGHT) { fprintf(stderr, "btree: maximum height exceeded\n"); goto fail; } new_root = allocate_page(bt, h); if (!new_root) goto fail; } /* Trace up to one page above the split. At each page that needs * splitting, copy the top half of keys into the new page. Also, * insert a key into one of the pages at all pages from the leaf * to the page above the top of the split. */ for (h = 0; h <= bt->root->height; h++) { int s = slot_old[h] + 1; struct btree_page *p = path_old[h]; /* If there's a split at this level, copy the top half of * the keys from the old page to the new one. Check to see * if the position we were going to insert into is in the * old page or the new one. */ if (path_new[h]) { split_page(path_old[h], path_new[h]); if (s > p->num_children) { s -= p->num_children; p = path_new[h]; } } /* Insert the key in the appropriate page */ if (h) insert_ptr(p, s, PAGE_KEY(path_new[h - 1], 0), path_new[h - 1]); else insert_data(p, s, key, data); /* If there was no split at this level, there's nothing to * insert higher up, and we're all done. */ if (!path_new[h]) return 0; } /* If we made it this far, the split reached the top of the tree, and * we need to grow it using the extra page we allocated. */ assert (new_root); if (bt->slot[0] >= 0) { /* Fix up the cursor, if active */ bt->slot[new_root->height] = bt->path[bt->root->height] == new_root ? 1 : 0; bt->path[new_root->height] = new_root; } memcpy(PAGE_KEY(new_root, 0), def->zero, def->key_size); *PAGE_PTR(new_root, 0) = path_old[h - 1]; memcpy(PAGE_KEY(new_root, 1), PAGE_KEY(path_new[h - 1], 0), def->key_size); *PAGE_PTR(new_root, 1) = path_new[h - 1]; new_root->num_children = 2; bt->root = new_root; return 0; fail: for (h = 0; h <= bt->root->height; h++) if (path_new[h]) free(path_new[h]); return -1; }
void btree_free(btree_t bt) { check_btree(bt); destroy_page(bt->root); free(bt); }
/*=========================================== * main -- Main procedure of dbverify command *=========================================*/ int main (int argc, char **argv) { char *flags, *dbname; char *ptr; char * msg; BOOLEAN cflag=FALSE; /* create new db if not found */ INT writ=1; /* request write access to database */ BOOLEAN immut=FALSE; /* immutable access to database */ BOOLEAN allchecks=FALSE; /* if user requested all checks */ INT returnvalue=1; STRING crashlog=NULL; INT lldberrnum=0; int i=0; /* initialize all the low-level library code */ init_stdlib(); validate_errs(); #if HAVE_SETLOCALE /* initialize locales */ setlocale(LC_ALL, ""); #endif /* HAVE_SETLOCALE */ #if ENABLE_NLS ll_bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif save_original_locales(); #ifdef WIN32 /* TO DO - research if this is necessary */ _fmode = O_BINARY; /* default to binary rather than TEXT mode */ #endif /* handle conventional arguments --version and --help */ /* needed for help2man to synthesize manual pages */ for (i=1; i<argc; ++i) { if (!strcmp(argv[i], "--version")) { print_version("dbverify"); return 0; } if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-?")) { print_usage(); return 0; } } if (argc != 3 || argv[1][0] != '-' || argv[1][1] == '\0') { print_usage(); goto done; } flags = argv[1]; dbname = argv[2]; for (ptr=&flags[1]; *ptr; ++ptr) { switch(*ptr) { case 'l': todo.check_dbstructure=TRUE; break; case 'g': todo.find_ghosts=TRUE; break; case 'G': todo.fix_ghosts=TRUE; break; case 'i': todo.check_indis=TRUE; break; case 'f': todo.check_fams=TRUE; break; case 's': todo.check_sours=TRUE; break; case 'e': todo.check_evens=TRUE; break; case 'x': todo.check_othes=TRUE; break; case 'n': noisy=TRUE; break; case 'a': allchecks=TRUE; break; case 'F': todo.fix_alter_pointers=TRUE; break; case 'm': todo.check_missing_data_records=TRUE; break; case 'M': todo.fix_missing_data_records=TRUE; break; case 'D': todo.fix_deletes=TRUE; break; case 'v': print_version("llexec"); goto done; case 'h': default: print_usage(); goto done; } } /* Turn off Memory Debugging */ /* This is unnecessary -- it is off anyway, Perry, 2001/10/28 */ alloclog = FALSE; /* Enable any checks needed for fixes selected */ if (todo.fix_missing_data_records) todo.check_missing_data_records = 1; /* initialize options & misc. stuff */ llgettext_set_default_localedir(LOCALEDIR); if (!init_lifelines_global(0, &msg, 0)) { printf("%s\n", msg); goto done; } /* setup crashlog in case init_screen fails (eg, bad menu shortcuts) */ crashlog = getlloptstr("CrashLog_dbverify", NULL); if (!crashlog) { crashlog = "Crashlog_dbverify.log"; } crash_setcrashlog(crashlog); /* NB: This assumes btree database */ if (!(BTR = bt_openbtree(dbname, cflag, writ, immut, &lldberrnum))) { char buffer[256]; describe_dberror(lldberrnum, buffer, ARRSIZE(buffer)); puts(buffer); goto done; } /* Do low-level checks before initializing lifelines database layer, because lifelines database init traverses btree for user options and translation tables, and here we check the infrastructure of the btree itself. */ if (todo.check_dbstructure || allchecks) { if (!check_btree(BTR)) goto done; } if (!init_lifelines_postdb()) { printf(_(qSbaddb)); goto done; } printf(_("Checking %s"), dbname); puts(""); /* all checks - if any new ones, have to update this */ if (allchecks) { todo.check_indis=todo.check_fams=todo.check_sours=TRUE; todo.check_evens=todo.check_othes=TRUE; todo.find_ghosts=TRUE; } if (todo.find_ghosts || todo.fix_ghosts) check_ghosts(); if (!(bwrite(BTR))) { todo.fix_alter_pointers = FALSE; todo.fix_ghosts = FALSE; } if (todo.check_indis || todo.check_fams || todo.check_sours || todo.check_evens || todo.check_othes) { check_and_fix_records(); } if (todo.check_missing_data_records) { check_missing_data_records(); } report_results(); closebtree(BTR); /* TODO: probably should call lldb_close, Perry 2005-10-07 */ BTR = 0; returnvalue = 0; done: return returnvalue; }