/* * __config_getraw -- * Given a config parser, find the final value for a given key. */ static int __config_getraw( WT_CONFIG *cparser, WT_CONFIG_ITEM *key, WT_CONFIG_ITEM *value, bool top) { WT_CONFIG sparser; WT_CONFIG_ITEM k, v, subk; WT_DECL_RET; bool found; found = false; while ((ret = __config_next(cparser, &k, &v)) == 0) { if (k.type != WT_CONFIG_ITEM_STRING && k.type != WT_CONFIG_ITEM_ID) continue; if (k.len == key->len && strncmp(key->str, k.str, k.len) == 0) { *value = v; found = true; } else if (k.len < key->len && key->str[k.len] == '.' && strncmp(key->str, k.str, k.len) == 0) { subk.str = key->str + k.len + 1; subk.len = (key->len - k.len) - 1; WT_RET(__wt_config_initn( cparser->session, &sparser, v.str, v.len)); if ((ret = __config_getraw( &sparser, &subk, value, false)) == 0) found = true; WT_RET_NOTFOUND_OK(ret); } } WT_RET_NOTFOUND_OK(ret); if (!found) return (WT_NOTFOUND); return (top ? __config_process_value(cparser, value) : 0); }
/* * __recovery_file_scan -- * Scan the files referenced from the metadata and gather information * about them for recovery. */ static int __recovery_file_scan(WT_RECOVERY *r) { WT_CURSOR *c; WT_DECL_RET; int cmp; const char *uri, *config; /* Scan through all files in the metadata. */ c = r->files[0].c; c->set_key(c, "file:"); if ((ret = c->search_near(c, &cmp)) != 0) { /* Is the metadata empty? */ WT_RET_NOTFOUND_OK(ret); return (0); } if (cmp < 0) WT_RET_NOTFOUND_OK(c->next(c)); for (; ret == 0; ret = c->next(c)) { WT_RET(c->get_key(c, &uri)); if (!WT_PREFIX_MATCH(uri, "file:")) break; WT_RET(c->get_value(c, &config)); WT_RET(__recovery_setup_file(r, uri, config)); } WT_RET_NOTFOUND_OK(ret); return (0); }
/* * __wt_struct_check -- * Check that the specified packing format is valid, and whether it fits * into a fixed-sized bitfield. */ int __wt_struct_check(WT_SESSION_IMPL *session, const char *fmt, size_t len, bool *fixedp, uint32_t *fixed_lenp) { WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; int fields; WT_RET(__pack_initn(session, &pack, fmt, len)); for (fields = 0; (ret = __pack_next(&pack, &pv)) == 0; fields++) ; WT_RET_NOTFOUND_OK(ret); if (fixedp != NULL && fixed_lenp != NULL) { if (fields == 0) { *fixedp = 1; *fixed_lenp = 0; } else if (fields == 1 && pv.type == 't') { *fixedp = 1; *fixed_lenp = pv.size; } else *fixedp = 0; } return (0); }
/* * __wt_config_gets_defno -- * Given a NULL-terminated list of configuration strings, and if the * application supplied any configuration strings, find the final value for * a given string key */ int __wt_config_gets_defno(WT_SESSION_IMPL *session, const char **cfg, const char *key, WT_CONFIG_ITEM *value) { static const WT_CONFIG_ITEM false_value = { "", 0, 0, ITEM_NUM }; /* * This is a performance hack: it's expensive to repeatedly parse * configuration strings, so don't do it unless it's necessary in * performance paths like cursor creation. Assume the second * configuration string is the application's configuration string, and * if it's not set (which is true most of the time), then assume the * default answer is "false", and return that. This makes it much * faster to open cursors when checking for obscure open configuration * strings like "next_random". */ *value = false_value; if (cfg == NULL || cfg[1] == NULL) return (0); else if (cfg[2] == NULL) WT_RET_NOTFOUND_OK( __wt_config_getones(session, cfg[1], key, value)); return (__wt_config_gets(session, cfg, key, value)); }
/* * __wt_config_get -- * Given a NULL-terminated list of configuration strings, find * the final value for a given key. */ int __wt_config_get(WT_SESSION_IMPL *session, const char **cfg_arg, WT_CONFIG_ITEM *key, WT_CONFIG_ITEM *value) { WT_CONFIG cparser; WT_DECL_RET; const char **cfg; if (cfg_arg[0] == NULL) return (WT_NOTFOUND); /* * Search the strings in reverse order, that way the first hit wins * and we don't search the base set until there's no other choice. */ for (cfg = cfg_arg; *cfg != NULL; ++cfg) ; do { --cfg; __wt_config_init(session, &cparser, *cfg); if ((ret = __config_getraw(&cparser, key, value, true)) == 0) return (0); WT_RET_NOTFOUND_OK(ret); } while (cfg != cfg_arg); return (WT_NOTFOUND); }
/* * __wt_schema_index_source -- * Get the URI of the data source for an index. */ int __wt_schema_index_source(WT_SESSION_IMPL *session, WT_TABLE *table, const char *idxname, const char *config, WT_ITEM *buf) { WT_CONFIG_ITEM cval; WT_DECL_RET; size_t len; const char *prefix, *suffix, *tablename; tablename = table->name + strlen("table:"); if ((ret = __wt_config_getones(session, config, "type", &cval)) == 0 && !WT_STRING_MATCH("file", cval.str, cval.len)) { prefix = cval.str; len = cval.len; suffix = "_idx"; } else { prefix = "file"; len = strlen(prefix); suffix = ".wti"; } WT_RET_NOTFOUND_OK(ret); WT_RET(__wt_buf_fmt(session, buf, "%.*s:%s_%s%s", (int)len, prefix, tablename, idxname, suffix)); return (0); }
/* * __cursor_truncate_fix -- * Discard a cursor range from fixed-width column-store tree. */ static int __cursor_truncate_fix(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *start, WT_CURSOR_BTREE *stop, int (*rmfunc)(WT_SESSION_IMPL *, WT_CURSOR_BTREE *, int)) { WT_DECL_RET; uint8_t *value; /* * Handle fixed-length column-store objects separately: for row-store * and variable-length column-store objects we have "deleted" values * and so returned objects actually exist: fixed-length column-store * objects are filled-in if they don't exist, that is, if you create * record 37, records 1-36 magically appear. Those records can't be * deleted, which means we have to ignore already "deleted" records. * * First, call the standard cursor remove method to do a full search and * re-position the cursor because we don't have a saved copy of the * page's write generation information, which we need to remove records. * Once that's done, we can delete records without a full search, unless * we encounter a restart error because the page was modified by some * other thread of control; in that case, repeat the full search to * refresh the page's modification information. */ if (start == NULL) { do { WT_RET(__wt_btcur_remove(stop)); for (;;) { if ((ret = __wt_btcur_prev(stop, 1)) != 0) break; stop->compare = 0; /* Exact match */ value = (uint8_t *)stop->iface.value.data; if (*value != 0 && (ret = rmfunc(session, stop, 1)) != 0) break; } } while (ret == WT_RESTART); } else { do { WT_RET(__wt_btcur_remove(start)); for (;;) { if (stop != NULL && __cursor_equals(start, stop)) break; if ((ret = __wt_btcur_next(start, 1)) != 0) break; start->compare = 0; /* Exact match */ value = (uint8_t *)start->iface.value.data; if (*value != 0 && (ret = rmfunc(session, start, 1)) != 0) break; } } while (ret == WT_RESTART); } WT_RET_NOTFOUND_OK(ret); return (0); }
/* * __cursor_truncate -- * Discard a cursor range from row-store or variable-width column-store * tree. */ static int __cursor_truncate(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *start, WT_CURSOR_BTREE *stop, int (*rmfunc)(WT_SESSION_IMPL *, WT_CURSOR_BTREE *, int)) { WT_DECL_RET; /* * First, call the standard cursor remove method to do a full search and * re-position the cursor because we don't have a saved copy of the * page's write generation information, which we need to remove records. * Once that's done, we can delete records without a full search, unless * we encounter a restart error because the page was modified by some * other thread of control; in that case, repeat the full search to * refresh the page's modification information. * * If this is a row-store, we delete leaf pages having no overflow items * without reading them; for that to work, we have to ensure we read the * page referenced by the ending cursor, since we may be deleting only a * partial page at the end of the truncation. Our caller already fully * instantiated the end cursor, so we know that page is pinned in memory * and we can proceed without concern. */ if (start == NULL) { do { WT_RET(__wt_btcur_remove(stop)); for (;;) { if ((ret = __wt_btcur_prev(stop, 1)) != 0) break; stop->compare = 0; /* Exact match */ if ((ret = rmfunc(session, stop, 1)) != 0) break; } } while (ret == WT_RESTART); } else { do { WT_RET(__wt_btcur_remove(start)); /* * Reset ret each time through so that we don't loop * forever in the cursor equals case. */ for (ret = 0;;) { if (stop != NULL && __cursor_equals(start, stop)) break; if ((ret = __wt_btcur_next(start, 1)) != 0) break; start->compare = 0; /* Exact match */ if ((ret = rmfunc(session, start, 1)) != 0) break; } } while (ret == WT_RESTART); } WT_RET_NOTFOUND_OK(ret); return (0); }
/* * __wt_meta_checkpoint_clear -- * Clear a file's checkpoint. */ int __wt_meta_checkpoint_clear(WT_SESSION_IMPL *session, const char *fname) { /* * If we are unrolling a failed create, we may have already removed the * metadata entry. If no entry is found to update and we're trying to * clear the checkpoint, just ignore it. */ WT_RET_NOTFOUND_OK(__ckpt_set(session, fname, NULL)); return (0); }
/* * __wt_range_truncate -- * Truncate of a cursor range, default implementation. */ int __wt_range_truncate(WT_CURSOR *start, WT_CURSOR *stop) { WT_DECL_RET; int cmp; if (start == NULL) { do { WT_RET(stop->remove(stop)); } while ((ret = stop->prev(stop)) == 0); WT_RET_NOTFOUND_OK(ret); } else { cmp = -1; do { if (stop != NULL) WT_RET(start->compare(start, stop, &cmp)); WT_RET(start->remove(start)); } while (cmp < 0 && (ret = start->next(start)) == 0); WT_RET_NOTFOUND_OK(ret); } return (0); }
/* * __json_struct_unpackv -- * Unpack a byte string to JSON (va_list version). */ static inline int __json_struct_unpackv(WT_SESSION_IMPL *session, const void *buffer, size_t size, const char *fmt, WT_CONFIG_ITEM *names, u_char *jbuf, size_t jbufsize, bool iskey, va_list ap) { WT_CONFIG_ITEM name; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; WT_PACK_NAME packname; size_t jsize; const uint8_t *p, *end; bool needcr; p = buffer; end = p + size; needcr = false; /* Unpacking a cursor marked as json implies a single arg. */ *va_arg(ap, const char **) = (char *)jbuf; __pack_name_init(session, names, iskey, &packname); WT_RET(__pack_init(session, &pack, fmt)); while ((ret = __pack_next(&pack, &pv)) == 0) { if (needcr) { WT_ASSERT(session, jbufsize >= 3); strncat((char *)jbuf, ",\n", jbufsize); jbuf += 2; jbufsize -= 2; } needcr = true; WT_RET(__unpack_read(session, &pv, &p, (size_t)(end - p))); WT_RET(__pack_name_next(&packname, &name)); jsize = 0; WT_RET(__json_unpack_put(session, (u_char *)&pv, jbuf, jbufsize, &name, &jsize)); WT_ASSERT(session, jsize <= jbufsize); jbuf += jsize; jbufsize -= jsize; } WT_RET_NOTFOUND_OK(ret); /* Be paranoid - __unpack_read should never overflow. */ WT_ASSERT(session, p <= end); WT_ASSERT(session, jbufsize == 1); return (0); }
/* * __wt_config_gets_def -- * Performance hack: skip parsing config strings by hard-coding defaults. * * It's expensive to repeatedly parse configuration strings, so don't do * it unless it's necessary in performance paths like cursor creation. * Assume the second configuration string is the application's * configuration string, and if it's not set (which is true most of the * time), then use the supplied default value. This makes it faster to * open cursors when checking for obscure open configuration strings like * "next_random". */ int __wt_config_gets_def(WT_SESSION_IMPL *session, const char **cfg, const char *key, int def, WT_CONFIG_ITEM *value) { static const WT_CONFIG_ITEM false_value = { "", 0, 0, WT_CONFIG_ITEM_NUM }; *value = false_value; value->val = def; if (cfg == NULL || cfg[0] == NULL || cfg[1] == NULL) return (0); else if (cfg[2] == NULL) WT_RET_NOTFOUND_OK( __wt_config_getones(session, cfg[1], key, value)); return (__wt_config_gets(session, cfg, key, value)); }
/* * __wt_struct_repack -- * Return the subset of the packed buffer that represents part of * the format. If the result is not contiguous in the existing * buffer, a buffer is reallocated and filled. */ int __wt_struct_repack(WT_SESSION_IMPL *session, const char *infmt, const char *outfmt, const WT_ITEM *inbuf, WT_ITEM *outbuf) { WT_DECL_PACK_VALUE(pvin); WT_DECL_PACK_VALUE(pvout); WT_DECL_RET; WT_PACK packin, packout; const uint8_t *before, *end, *p; const void *start; start = NULL; p = inbuf->data; end = p + inbuf->size; WT_RET(__pack_init(session, &packout, outfmt)); WT_RET(__pack_init(session, &packin, infmt)); /* Outfmt should complete before infmt */ while ((ret = __pack_next(&packout, &pvout)) == 0) { if (p >= end) WT_RET(EINVAL); if (pvout.type == 'x' && pvout.size == 0 && pvout.havesize) continue; WT_RET(__pack_next(&packin, &pvin)); before = p; WT_RET(__unpack_read(session, &pvin, &p, (size_t)(end - p))); if (pvout.type != pvin.type) WT_RET(ENOTSUP); if (start == NULL) start = before; } WT_RET_NOTFOUND_OK(ret); /* Be paranoid - __pack_write should never overflow. */ WT_ASSERT(session, p <= end); outbuf->data = start; outbuf->size = WT_PTRDIFF(p, start); return (0); }
/* * __conn_verbose_config -- * Set verbose configuration. */ static int __conn_verbose_config(WT_SESSION_IMPL *session, const char *cfg[]) { WT_CONFIG_ITEM cval, sval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; static struct { const char *name; uint32_t flag; } *ft, verbtypes[] = { { "block", WT_VERB_block }, { "shared_cache",WT_VERB_shared_cache }, { "ckpt", WT_VERB_ckpt }, { "evict", WT_VERB_evict }, { "evictserver",WT_VERB_evictserver }, { "fileops", WT_VERB_fileops }, { "hazard", WT_VERB_hazard }, { "lsm", WT_VERB_lsm }, { "mutex", WT_VERB_mutex }, { "read", WT_VERB_read }, { "reconcile", WT_VERB_reconcile }, { "salvage", WT_VERB_salvage }, { "verify", WT_VERB_verify }, { "write", WT_VERB_write }, { NULL, 0 } }; conn = S2C(session); if ((ret = __wt_config_gets(session, cfg, "verbose", &cval)) != 0) return (ret == WT_NOTFOUND ? 0 : ret); for (ft = verbtypes; ft->name != NULL; ft++) { if ((ret = __wt_config_subgets( session, &cval, ft->name, &sval)) == 0 && sval.val != 0) FLD_SET(conn->verbose, ft->flag); else FLD_CLR(conn->verbose, ft->flag); WT_RET_NOTFOUND_OK(ret); } return (0); }
/* * __meta_btree_apply -- * Apply a function to all files listed in the metadata, apart from the * metadata file. */ static inline int __meta_btree_apply(WT_SESSION_IMPL *session, WT_CURSOR *cursor, int (*func)(WT_SESSION_IMPL *, const char *[]), const char *cfg[]) { WT_DECL_RET; const char *uri; int cmp; cursor->set_key(cursor, "file:"); if ((ret = cursor->search_near(cursor, &cmp)) == 0 && cmp < 0) ret = cursor->next(cursor); for (; ret == 0; ret = cursor->next(cursor)) { WT_RET(cursor->get_key(cursor, &uri)); if (!WT_PREFIX_MATCH(uri, "file:")) break; if (strcmp(uri, WT_METAFILE_URI) == 0) continue; /* * We need to pull the handle into the session handle cache * and make sure it's referenced to stop other internal code * dropping the handle (e.g in LSM when cleaning up obsolete * chunks). Holding the metadata lock isn't enough. */ ret = __wt_session_get_btree(session, uri, NULL, NULL, 0); if (ret == 0) { WT_SAVE_DHANDLE(session, ret = func(session, cfg)); if (WT_META_TRACKING(session)) WT_TRET(__wt_meta_track_handle_lock( session, false)); else WT_TRET(__wt_session_release_btree(session)); } else if (ret == EBUSY) ret = __wt_conn_btree_apply_single( session, uri, NULL, func, cfg); WT_RET(ret); } WT_RET_NOTFOUND_OK(ret); return (0); }
/* * __wt_txn_reconfigure -- * WT_SESSION::reconfigure for transactions. */ int __wt_txn_reconfigure(WT_SESSION_IMPL *session, const char *config) { WT_CONFIG_ITEM cval; WT_DECL_RET; WT_TXN *txn; txn = &session->txn; ret = __wt_config_getones(session, config, "isolation", &cval); if (ret == 0 && cval.len != 0) { session->isolation = txn->isolation = WT_STRING_MATCH("snapshot", cval.str, cval.len) ? WT_ISO_SNAPSHOT : WT_STRING_MATCH("read-uncommitted", cval.str, cval.len) ? WT_ISO_READ_UNCOMMITTED : WT_ISO_READ_COMMITTED; } WT_RET_NOTFOUND_OK(ret); return (0); }
/* * __wt_schema_colcheck -- * Check that a list of columns matches a (key,value) format pair. */ int __wt_schema_colcheck(WT_SESSION_IMPL *session, const char *key_format, const char *value_format, WT_CONFIG_ITEM *colconf, u_int *kcolsp, u_int *vcolsp) { WT_CONFIG conf; WT_CONFIG_ITEM k, v; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; u_int kcols, ncols, vcols; WT_RET(__pack_init(session, &pack, key_format)); for (kcols = 0; (ret = __pack_next(&pack, &pv)) == 0; kcols++) ; WT_RET_NOTFOUND_OK(ret); WT_RET(__pack_init(session, &pack, value_format)); for (vcols = 0; (ret = __pack_next(&pack, &pv)) == 0; vcols++) ; WT_RET_TEST(ret != WT_NOTFOUND, ret); /* Walk through the named columns. */ __wt_config_subinit(session, &conf, colconf); for (ncols = 0; (ret = __wt_config_next(&conf, &k, &v)) == 0; ncols++) ; WT_RET_TEST(ret != WT_NOTFOUND, ret); if (ncols != 0 && ncols != kcols + vcols) WT_RET_MSG(session, EINVAL, "Number of columns in '%.*s' " "does not match key format '%s' plus value format '%s'", (int)colconf->len, colconf->str, key_format, value_format); if (kcolsp != NULL) *kcolsp = kcols; if (vcolsp != NULL) *vcolsp = vcols; return (0); }
/* * __meta_btree_apply -- * Apply a function to all files listed in the metadata, apart from the * metadata file. */ static inline int __meta_btree_apply(WT_SESSION_IMPL *session, WT_CURSOR *cursor, int (*file_func)(WT_SESSION_IMPL *, const char *[]), int (*name_func)(WT_SESSION_IMPL *, const char *, bool *), const char *cfg[]) { WT_DECL_RET; const char *uri; bool skip; while ((ret = cursor->next(cursor)) == 0) { WT_RET(cursor->get_key(cursor, &uri)); if (strcmp(uri, WT_METAFILE_URI) == 0) continue; skip = false; if (name_func != NULL) WT_RET(name_func(session, uri, &skip)); if (file_func == NULL || skip || !WT_PREFIX_MATCH(uri, "file:")) continue; /* * We need to pull the handle into the session handle cache * and make sure it's referenced to stop other internal code * dropping the handle (e.g in LSM when cleaning up obsolete * chunks). Holding the metadata lock isn't enough. */ if ((ret = __wt_session_get_btree( session, uri, NULL, NULL, 0)) != 0) return (ret == EBUSY ? 0 : ret); WT_SAVE_DHANDLE(session, ret = file_func(session, cfg)); WT_TRET(__wt_session_release_btree(session)); WT_RET(ret); } WT_RET_NOTFOUND_OK(ret); return (0); }
/* * __json_struct_size -- * Calculate the size of a packed byte string as formatted for JSON. */ static inline int __json_struct_size(WT_SESSION_IMPL *session, const void *buffer, size_t size, const char *fmt, WT_CONFIG_ITEM *names, bool iskey, size_t *presult) { WT_CONFIG_ITEM name; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; WT_PACK_NAME packname; size_t result; const uint8_t *p, *end; bool needcr; p = buffer; end = p + size; result = 0; needcr = false; __pack_name_init(session, names, iskey, &packname); WT_RET(__pack_init(session, &pack, fmt)); while ((ret = __pack_next(&pack, &pv)) == 0) { if (needcr) result += 2; needcr = true; WT_RET(__unpack_read(session, &pv, &p, (size_t)(end - p))); WT_RET(__pack_name_next(&packname, &name)); WT_RET( __json_unpack_put(session, &pv, NULL, 0, &name, &result)); } WT_RET_NOTFOUND_OK(ret); /* Be paranoid - __pack_write should never overflow. */ WT_ASSERT(session, p <= end); *presult = result; return (0); }
/* * __wt_config_gets_def -- * Performance hack: skip parsing config strings by hard-coding defaults. * * It's expensive to repeatedly parse configuration strings, so don't do * it unless it's necessary in performance paths like cursor creation. * Assume the second configuration string is the application's * configuration string, and if it's not set (which is true most of the * time), then use the supplied default value. This makes it faster to * open cursors when checking for obscure open configuration strings like * "next_random". */ int __wt_config_gets_def(WT_SESSION_IMPL *session, const char **cfg, const char *key, int def, WT_CONFIG_ITEM *value) { WT_CONFIG_ITEM_STATIC_INIT(false_value); const char **end; *value = false_value; value->val = def; if (cfg == NULL) return (0); /* * Checking the "length" of the pointer array is a little odd, but it's * deliberate. The reason is because we pass variable length arrays of * pointers as the configuration argument, some of which have only one * element and the NULL termination. Static analyzers (like Coverity) * complain if we read from an offset past the end of the array, even * if we check there's no NULL slots before the offset. */ for (end = cfg; *end != NULL; ++end) ; switch ((int)(end - cfg)) { case 0: /* cfg[0] == NULL */ case 1: /* cfg[1] == NULL */ return (0); case 2: /* cfg[2] == NULL */ WT_RET_NOTFOUND_OK( __wt_config_getones(session, cfg[1], key, value)); return (0); default: return (__wt_config_gets(session, cfg, key, value)); } /* NOTREACHED */ }
/* * __wt_lsm_tree_create -- * Create an LSM tree structure for the given name. */ int __wt_lsm_tree_create(WT_SESSION_IMPL *session, const char *uri, int exclusive, const char *config) { WT_CONFIG_ITEM cval; WT_DECL_ITEM(buf); WT_DECL_RET; WT_LSM_TREE *lsm_tree; const char *cfg[] = { WT_CONFIG_BASE(session, session_create), config, NULL }; char *tmpconfig; /* If the tree is open, it already exists. */ if ((ret = __wt_lsm_tree_get(session, uri, 0, &lsm_tree)) == 0) { __wt_lsm_tree_release(session, lsm_tree); return (exclusive ? EEXIST : 0); } WT_RET_NOTFOUND_OK(ret); /* * If the tree has metadata, it already exists. * * !!! * Use a local variable: we don't care what the existing configuration * is, but we don't want to overwrite the real config. */ if (__wt_metadata_search(session, uri, &tmpconfig) == 0) { __wt_free(session, tmpconfig); return (exclusive ? EEXIST : 0); } WT_RET_NOTFOUND_OK(ret); WT_RET(__wt_config_gets(session, cfg, "key_format", &cval)); if (WT_STRING_MATCH("r", cval.str, cval.len)) WT_RET_MSG(session, EINVAL, "LSM trees cannot be configured as column stores"); WT_RET(__wt_calloc_def(session, 1, &lsm_tree)); WT_ERR(__lsm_tree_set_name(session, lsm_tree, uri)); WT_ERR(__wt_config_gets(session, cfg, "key_format", &cval)); WT_ERR(__wt_strndup( session, cval.str, cval.len, &lsm_tree->key_format)); WT_ERR(__wt_config_gets(session, cfg, "value_format", &cval)); WT_ERR(__wt_strndup( session, cval.str, cval.len, &lsm_tree->value_format)); WT_ERR(__wt_config_gets(session, cfg, "collator", &cval)); WT_ERR(__wt_strndup( session, cval.str, cval.len, &lsm_tree->collator_name)); WT_ERR(__wt_config_gets(session, cfg, "lsm.auto_throttle", &cval)); if (cval.val) F_SET(lsm_tree, WT_LSM_TREE_THROTTLE); else F_CLR(lsm_tree, WT_LSM_TREE_THROTTLE); WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom", &cval)); FLD_SET(lsm_tree->bloom, (cval.val == 0 ? WT_LSM_BLOOM_OFF : WT_LSM_BLOOM_MERGED)); WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom_oldest", &cval)); if (cval.val != 0) FLD_SET(lsm_tree->bloom, WT_LSM_BLOOM_OLDEST); if (FLD_ISSET(lsm_tree->bloom, WT_LSM_BLOOM_OFF) && FLD_ISSET(lsm_tree->bloom, WT_LSM_BLOOM_OLDEST)) WT_ERR_MSG(session, EINVAL, "Bloom filters can only be created on newest and oldest " "chunks if bloom filters are enabled"); WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom_config", &cval)); if (cval.type == WT_CONFIG_ITEM_STRUCT) { cval.str++; cval.len -= 2; } WT_ERR(__wt_strndup( session, cval.str, cval.len, &lsm_tree->bloom_config)); WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom_bit_count", &cval)); lsm_tree->bloom_bit_count = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom_hash_count", &cval)); lsm_tree->bloom_hash_count = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm.chunk_max", &cval)); lsm_tree->chunk_max = (uint64_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm.chunk_size", &cval)); lsm_tree->chunk_size = (uint64_t)cval.val; if (lsm_tree->chunk_size > lsm_tree->chunk_max) WT_ERR_MSG(session, EINVAL, "Chunk size (chunk_size) must be smaller than or equal to " "the maximum chunk size (chunk_max)"); WT_ERR(__wt_config_gets(session, cfg, "lsm.merge_max", &cval)); lsm_tree->merge_max = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm.merge_min", &cval)); lsm_tree->merge_min = (uint32_t)cval.val; if (lsm_tree->merge_min > lsm_tree->merge_max) WT_ERR_MSG(session, EINVAL, "LSM merge_min must be less than or equal to merge_max"); /* * Set up the config for each chunk. * * Make the memory_page_max double the chunk size, so application * threads don't immediately try to force evict the chunk when the * worker thread clears the NO_EVICTION flag. */ WT_ERR(__wt_scr_alloc(session, 0, &buf)); WT_ERR(__wt_buf_fmt(session, buf, "%s,key_format=u,value_format=u,memory_page_max=%" PRIu64, config, 2 * lsm_tree->chunk_max)); WT_ERR(__wt_strndup( session, buf->data, buf->size, &lsm_tree->file_config)); /* Create the first chunk and flush the metadata. */ WT_ERR(__wt_lsm_meta_write(session, lsm_tree)); /* Discard our partially populated handle. */ ret = __lsm_tree_discard(session, lsm_tree); lsm_tree = NULL; /* * Open our new tree and add it to the handle cache. Don't discard on * error: the returned handle is NULL on error, and the metadata * tracking macros handle cleaning up on failure. */ if (ret == 0) ret = __lsm_tree_open(session, uri, &lsm_tree); if (ret == 0) __wt_lsm_tree_release(session, lsm_tree); if (0) { err: WT_TRET(__lsm_tree_discard(session, lsm_tree)); } __wt_scr_free(&buf); return (ret); }
/* * __create_table -- * Create a table. */ static int __create_table(WT_SESSION_IMPL *session, const char *name, bool exclusive, const char *config) { WT_CONFIG conf; WT_CONFIG_ITEM cgkey, cgval, cval; WT_DECL_RET; WT_TABLE *table; const char *cfg[4] = { WT_CONFIG_BASE(session, table_meta), config, NULL, NULL }; const char *tablename; char *tableconf, *cgname; size_t cgsize; int ncolgroups; bool exists; cgname = NULL; table = NULL; tableconf = NULL; exists = false; tablename = name; if (!WT_PREFIX_SKIP(tablename, "table:")) return (EINVAL); if ((ret = __wt_schema_get_table(session, tablename, strlen(tablename), false, &table)) == 0) { if (exclusive) WT_ERR(EEXIST); exists = true; } WT_RET_NOTFOUND_OK(ret); WT_ERR(__wt_config_gets(session, cfg, "colgroups", &cval)); WT_ERR(__wt_config_subinit(session, &conf, &cval)); for (ncolgroups = 0; (ret = __wt_config_next(&conf, &cgkey, &cgval)) == 0; ncolgroups++) ; WT_ERR_NOTFOUND_OK(ret); WT_ERR(__wt_config_collapse(session, cfg, &tableconf)); if (exists) { if (strcmp(tableconf, table->config) != 0) WT_ERR_MSG(session, EINVAL, "%s: does not match existing configuration", name); goto err; } WT_ERR(__wt_metadata_insert(session, name, tableconf)); /* Attempt to open the table now to catch any errors. */ WT_ERR(__wt_schema_get_table( session, tablename, strlen(tablename), true, &table)); if (ncolgroups == 0) { cgsize = strlen("colgroup:") + strlen(tablename) + 1; WT_ERR(__wt_calloc_def(session, cgsize, &cgname)); snprintf(cgname, cgsize, "colgroup:%s", tablename); WT_ERR(__create_colgroup(session, cgname, exclusive, config)); } if (0) { err: if (table != NULL) { WT_TRET(__wt_schema_remove_table(session, table)); table = NULL; } } if (table != NULL) __wt_schema_release_table(session, table); __wt_free(session, cgname); __wt_free(session, tableconf); return (ret); }
/* * __wt_lsm_tree_create -- * Create an LSM tree structure for the given name. */ int __wt_lsm_tree_create(WT_SESSION_IMPL *session, const char *uri, int exclusive, const char *config) { WT_CONFIG_ITEM cval; WT_DECL_ITEM(buf); WT_DECL_RET; WT_LSM_TREE *lsm_tree; const char *cfg[] = { WT_CONFIG_BASE(session, session_create), config, NULL }; const char *tmpconfig; /* If the tree is open, it already exists. */ if ((ret = __wt_lsm_tree_get(session, uri, 0, &lsm_tree)) == 0) { __wt_lsm_tree_release(session, lsm_tree); return (exclusive ? EEXIST : 0); } WT_RET_NOTFOUND_OK(ret); /* * If the tree has metadata, it already exists. * * !!! * Use a local variable: we don't care what the existing configuration * is, but we don't want to overwrite the real config. */ if (__wt_metadata_search(session, uri, &tmpconfig) == 0) { __wt_free(session, tmpconfig); return (exclusive ? EEXIST : 0); } WT_RET_NOTFOUND_OK(ret); WT_RET(__wt_config_gets(session, cfg, "key_format", &cval)); if (WT_STRING_MATCH("r", cval.str, cval.len)) WT_RET_MSG(session, EINVAL, "LSM trees cannot be configured as column stores"); WT_RET(__wt_calloc_def(session, 1, &lsm_tree)); WT_ERR(__lsm_tree_set_name(session, lsm_tree, uri)); WT_ERR(__wt_config_gets(session, cfg, "key_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &lsm_tree->key_format)); WT_ERR(__wt_config_gets(session, cfg, "value_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &lsm_tree->value_format)); WT_ERR(__wt_config_gets(session, cfg, "collator", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &lsm_tree->collator_name)); WT_ERR(__wt_config_gets(session, cfg, "lsm.auto_throttle", &cval)); if (cval.val) F_SET(lsm_tree, WT_LSM_TREE_THROTTLE); else F_CLR(lsm_tree, WT_LSM_TREE_THROTTLE); WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom", &cval)); FLD_SET(lsm_tree->bloom, (cval.val == 0 ? WT_LSM_BLOOM_OFF : WT_LSM_BLOOM_MERGED)); WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom_oldest", &cval)); if (cval.val != 0) FLD_SET(lsm_tree->bloom, WT_LSM_BLOOM_OLDEST); if (FLD_ISSET(lsm_tree->bloom, WT_LSM_BLOOM_OFF) && FLD_ISSET(lsm_tree->bloom, WT_LSM_BLOOM_OLDEST)) WT_ERR_MSG(session, EINVAL, "Bloom filters can only be created on newest and oldest " "chunks if bloom filters are enabled"); WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom_config", &cval)); if (cval.type == WT_CONFIG_ITEM_STRUCT) { cval.str++; cval.len -= 2; } WT_ERR(__wt_strndup(session, cval.str, cval.len, &lsm_tree->bloom_config)); WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom_bit_count", &cval)); lsm_tree->bloom_bit_count = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm.bloom_hash_count", &cval)); lsm_tree->bloom_hash_count = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm.chunk_max", &cval)); lsm_tree->chunk_max = (uint64_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm.chunk_size", &cval)); lsm_tree->chunk_size = (uint64_t)cval.val; if (lsm_tree->chunk_size > lsm_tree->chunk_max) WT_ERR_MSG(session, EINVAL, "Chunk size (chunk_size) must be smaller than or equal to " "the maximum chunk size (chunk_max)"); WT_ERR(__wt_config_gets(session, cfg, "lsm.merge_max", &cval)); lsm_tree->merge_max = (uint32_t)cval.val; lsm_tree->merge_min = lsm_tree->merge_max / 2; WT_ERR(__wt_config_gets(session, cfg, "lsm.merge_threads", &cval)); lsm_tree->merge_threads = (uint32_t)cval.val; /* Sanity check that api_data.py is in sync with lsm.h */ WT_ASSERT(session, lsm_tree->merge_threads <= WT_LSM_MAX_WORKERS); /* * Set up the config for each chunk. If possible, avoid high latencies * from fsync by flushing the cache every 8MB (will be overridden by * any application setting). */ tmpconfig = ""; #ifdef HAVE_SYNC_FILE_RANGE if (!S2C(session)->direct_io) tmpconfig = "os_cache_dirty_max=8MB,"; #endif WT_ERR(__wt_scr_alloc(session, 0, &buf)); WT_ERR(__wt_buf_fmt(session, buf, "%s%s,key_format=u,value_format=u", tmpconfig, config)); lsm_tree->file_config = __wt_buf_steal(session, buf, NULL); /* Create the first chunk and flush the metadata. */ WT_ERR(__wt_lsm_meta_write(session, lsm_tree)); /* Discard our partially populated handle. */ ret = __lsm_tree_discard(session, lsm_tree); lsm_tree = NULL; /* * Open our new tree and add it to the handle cache. Don't discard on * error: the returned handle is NULL on error, and the metadata * tracking macros handle cleaning up on failure. */ if (ret == 0) ret = __lsm_tree_open(session, uri, &lsm_tree); if (ret == 0) __wt_lsm_tree_release(session, lsm_tree); if (0) { err: WT_TRET(__lsm_tree_discard(session, lsm_tree)); } __wt_scr_free(&buf); return (ret); }
/* * __wt_lsm_tree_create -- * Create an LSM tree structure for the given name. */ int __wt_lsm_tree_create(WT_SESSION_IMPL *session, const char *uri, int exclusive, const char *config) { WT_CONFIG_ITEM cval; WT_DECL_ITEM(buf); WT_DECL_RET; WT_LSM_TREE *lsm_tree; const char *cfg[] = API_CONF_DEFAULTS(session, create, config); const char *tmpconfig; /* If the tree is open, it already exists. */ if ((ret = __wt_lsm_tree_get(session, uri, 0, &lsm_tree)) == 0) { __wt_lsm_tree_release(session, lsm_tree); return (exclusive ? EEXIST : 0); } WT_RET_NOTFOUND_OK(ret); /* * If the tree has metadata, it already exists. * * !!! * Use a local variable: we don't care what the existing configuration * is, but we don't want to overwrite the real config. */ if (__wt_metadata_read(session, uri, &tmpconfig) == 0) { __wt_free(session, tmpconfig); return (exclusive ? EEXIST : 0); } WT_RET_NOTFOUND_OK(ret); WT_RET(__wt_config_gets(session, cfg, "key_format", &cval)); if (WT_STRING_MATCH("r", cval.str, cval.len)) WT_RET_MSG(session, EINVAL, "LSM trees cannot be configured as column stores"); WT_RET(__wt_calloc_def(session, 1, &lsm_tree)); WT_RET(__lsm_tree_set_name(session, lsm_tree, uri)); WT_ERR(__wt_config_gets(session, cfg, "key_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &lsm_tree->key_format)); WT_ERR(__wt_config_gets(session, cfg, "value_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &lsm_tree->value_format)); WT_ERR(__wt_config_gets(session, cfg, "lsm_bloom", &cval)); FLD_SET(lsm_tree->bloom, (cval.val == 0 ? WT_LSM_BLOOM_OFF : WT_LSM_BLOOM_MERGED)); WT_ERR(__wt_config_gets(session, cfg, "lsm_bloom_newest", &cval)); if (cval.val != 0) FLD_SET(lsm_tree->bloom, WT_LSM_BLOOM_NEWEST); WT_ERR(__wt_config_gets(session, cfg, "lsm_bloom_oldest", &cval)); if (cval.val != 0) FLD_SET(lsm_tree->bloom, WT_LSM_BLOOM_OLDEST); if (FLD_ISSET(lsm_tree->bloom, WT_LSM_BLOOM_OFF) && (FLD_ISSET(lsm_tree->bloom, WT_LSM_BLOOM_NEWEST) || FLD_ISSET(lsm_tree->bloom, WT_LSM_BLOOM_OLDEST))) WT_ERR_MSG(session, EINVAL, "Bloom filters can only be created on newest and oldest " "chunks if bloom filters are enabled"); WT_ERR(__wt_config_gets(session, cfg, "lsm_bloom_config", &cval)); if (cval.type == ITEM_STRUCT) { cval.str++; cval.len -= 2; } WT_ERR(__wt_strndup(session, cval.str, cval.len, &lsm_tree->bloom_config)); WT_ERR(__wt_config_gets(session, cfg, "lsm_bloom_bit_count", &cval)); lsm_tree->bloom_bit_count = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm_bloom_hash_count", &cval)); lsm_tree->bloom_hash_count = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm_chunk_size", &cval)); lsm_tree->chunk_size = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm_merge_max", &cval)); lsm_tree->merge_max = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "lsm_merge_threads", &cval)); lsm_tree->merge_threads = (uint32_t)cval.val; /* Sanity check that api_data.py is in sync with lsm.h */ WT_ASSERT(session, lsm_tree->merge_threads <= WT_LSM_MAX_WORKERS); WT_ERR(__wt_scr_alloc(session, 0, &buf)); WT_ERR(__wt_buf_fmt(session, buf, "%s,key_format=u,value_format=u", config)); lsm_tree->file_config = __wt_buf_steal(session, buf, NULL); /* Create the first chunk and flush the metadata. */ WT_ERR(__wt_lsm_meta_write(session, lsm_tree)); /* Discard our partially populated handle. */ ret = __lsm_tree_discard(session, lsm_tree); lsm_tree = NULL; /* * Open our new tree and add it to the handle cache. Don't discard on * error: the returned handle is NULL on error, and the metadata * tracking macros handle cleaning up on failure. */ if (ret == 0) ret = __lsm_tree_open(session, uri, &lsm_tree); if (ret == 0) __wt_lsm_tree_release(session, lsm_tree); if (0) { err: WT_TRET(__lsm_tree_discard(session, lsm_tree)); } __wt_scr_free(&buf); return (ret); }
/* * __conn_dhandle_get -- * Allocate a new data handle, lock it exclusively, and return it linked * into the connection's list. */ static int __conn_dhandle_get(WT_SESSION_IMPL *session, const char *name, const char *ckpt, uint32_t flags) { WT_BTREE *btree; WT_CONNECTION_IMPL *conn; WT_DATA_HANDLE *dhandle; WT_DECL_RET; uint32_t bucket; conn = S2C(session); /* * We have the handle lock, check whether we can find the handle we * are looking for. If we do, and we can lock it in the state we * want, this session will take ownership and we are done. */ ret = __wt_conn_dhandle_find(session, name, ckpt, flags); if (ret == 0) { dhandle = session->dhandle; WT_RET(__conn_dhandle_open_lock(session, dhandle, flags)); return (0); } WT_RET_NOTFOUND_OK(ret); /* * If no handle was found, allocate the data handle and a btree handle, * then initialize the data handle. Exclusively lock the data handle * before inserting it in the list. */ WT_RET(__wt_calloc_one(session, &dhandle)); WT_ERR(__wt_rwlock_alloc(session, &dhandle->rwlock, "data handle")); dhandle->name_hash = __wt_hash_city64(name, strlen(name)); WT_ERR(__wt_strdup(session, name, &dhandle->name)); if (ckpt != NULL) WT_ERR(__wt_strdup(session, ckpt, &dhandle->checkpoint)); WT_ERR(__wt_calloc_one(session, &btree)); dhandle->handle = btree; btree->dhandle = dhandle; WT_ERR(__wt_spin_init( session, &dhandle->close_lock, "data handle close")); F_SET(dhandle, WT_DHANDLE_EXCLUSIVE); WT_ERR(__wt_writelock(session, dhandle->rwlock)); /* * Prepend the handle to the connection list, assuming we're likely to * need new files again soon, until they are cached by all sessions. * Find the right hash bucket to insert into as well. */ WT_ASSERT(session, F_ISSET(session, WT_SESSION_HANDLE_LIST_LOCKED)); bucket = dhandle->name_hash % WT_HASH_ARRAY_SIZE; WT_CONN_DHANDLE_INSERT(conn, dhandle, bucket); session->dhandle = dhandle; return (0); err: WT_TRET(__wt_rwlock_destroy(session, &dhandle->rwlock)); __wt_free(session, dhandle->name); __wt_free(session, dhandle->checkpoint); __wt_free(session, dhandle->handle); /* btree free */ __wt_spin_destroy(session, &dhandle->close_lock); __wt_overwrite_and_free(session, dhandle); return (ret); }
/* * __create_table -- * Create a table. */ static int __create_table(WT_SESSION_IMPL *session, const char *name, int exclusive, const char *config) { WT_CONFIG conf; WT_CONFIG_ITEM cgkey, cgval, cval; WT_DECL_RET; WT_TABLE *table; const char *cfg[4] = { WT_CONFIG_BASE(session, table_meta), config, NULL, NULL }; const char *tablename; char *tableconf, *cgname; size_t cgsize; int ncolgroups; cgname = NULL; table = NULL; tableconf = NULL; tablename = name; if (!WT_PREFIX_SKIP(tablename, "table:")) return (EINVAL); if ((ret = __wt_schema_get_table(session, tablename, strlen(tablename), 0, &table)) == 0) { __wt_schema_release_table(session, table); return (exclusive ? EEXIST : 0); } WT_RET_NOTFOUND_OK(ret); WT_ERR(__wt_config_gets(session, cfg, "colgroups", &cval)); WT_ERR(__wt_config_subinit(session, &conf, &cval)); for (ncolgroups = 0; (ret = __wt_config_next(&conf, &cgkey, &cgval)) == 0; ncolgroups++) ; WT_ERR_NOTFOUND_OK(ret); WT_ERR(__wt_config_collapse(session, cfg, &tableconf)); if ((ret = __wt_metadata_insert(session, name, tableconf)) != 0) { /* * If the entry already exists in the metadata, we're done. * This is an error for exclusive creates but okay otherwise. */ if (ret == WT_DUPLICATE_KEY) ret = exclusive ? EEXIST : 0; goto err; } /* Attempt to open the table now to catch any errors. */ WT_ERR(__wt_schema_get_table( session, tablename, strlen(tablename), 1, &table)); if (ncolgroups == 0) { cgsize = strlen("colgroup:") + strlen(tablename) + 1; WT_ERR(__wt_calloc_def(session, cgsize, &cgname)); snprintf(cgname, cgsize, "colgroup:%s", tablename); WT_ERR(__create_colgroup(session, cgname, exclusive, config)); } if (0) { err: if (table != NULL) { WT_TRET(__wt_schema_remove_table(session, table)); table = NULL; } } if (table != NULL) __wt_schema_release_table(session, table); __wt_free(session, cgname); __wt_free(session, tableconf); return (ret); }
/* * __wt_struct_reformat -- * Given a table and a list of columns (which could be values in a column * group or index keys), calculate the resulting new format string. * The result will be appended to the format buffer. */ int __wt_struct_reformat(WT_SESSION_IMPL *session, WT_TABLE *table, const char *columns, size_t len, const char *extra_cols, bool value_only, WT_ITEM *format) { WT_CONFIG config; WT_CONFIG_ITEM k, next_k, next_v; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; bool have_next; __wt_config_initn(session, &config, columns, len); /* * If an empty column list is specified, this will fail with * WT_NOTFOUND, that's okay. */ WT_RET_NOTFOUND_OK(ret = __wt_config_next(&config, &next_k, &next_v)); if (ret == WT_NOTFOUND) { if (extra_cols != NULL) { __wt_config_init(session, &config, extra_cols); WT_RET(__wt_config_next(&config, &next_k, &next_v)); extra_cols = NULL; } else if (format->size == 0) { WT_RET(__wt_buf_set(session, format, "", 1)); return (0); } } do { k = next_k; ret = __wt_config_next(&config, &next_k, &next_v); if (ret != 0 && ret != WT_NOTFOUND) return (ret); have_next = ret == 0; if (!have_next && extra_cols != NULL) { __wt_config_init(session, &config, extra_cols); WT_RET(__wt_config_next(&config, &next_k, &next_v)); have_next = true; extra_cols = NULL; } if ((ret = __find_column_format(session, table, &k, value_only, &pv)) != 0) { if (value_only && ret == EINVAL) WT_RET_MSG(session, EINVAL, "A column group cannot store key column " "'%.*s' in its value", (int)k.len, k.str); WT_RET_MSG(session, EINVAL, "Column '%.*s' not found", (int)k.len, k.str); } /* * Check whether we're moving an unsized WT_ITEM from the end * to the middle, or vice-versa. This determines whether the * size needs to be prepended. This is the only case where the * destination size can be larger than the source size. */ if (pv.type == 'u' && !pv.havesize && have_next) pv.type = 'U'; else if (pv.type == 'U' && !have_next) pv.type = 'u'; if (pv.havesize) WT_RET(__wt_buf_catfmt(session, format, "%" PRIu32 "%c", pv.size, pv.type)); else WT_RET(__wt_buf_catfmt(session, format, "%c", pv.type)); } while (have_next); return (0); }
/* * __wt_conn_statistics_config -- * Set statistics configuration. */ int __wt_conn_statistics_config(WT_SESSION_IMPL *session, const char *cfg[]) { WT_CONFIG_ITEM cval, sval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; uint32_t flags; int set; conn = S2C(session); WT_RET(__wt_config_gets(session, cfg, "statistics", &cval)); flags = 0; set = 0; if ((ret = __wt_config_subgets( session, &cval, "none", &sval)) == 0 && sval.val != 0) { flags = 0; ++set; } WT_RET_NOTFOUND_OK(ret); if ((ret = __wt_config_subgets( session, &cval, "fast", &sval)) == 0 && sval.val != 0) { LF_SET(WT_STAT_TYPE_FAST); ++set; } WT_RET_NOTFOUND_OK(ret); if ((ret = __wt_config_subgets( session, &cval, "all", &sval)) == 0 && sval.val != 0) { LF_SET( WT_STAT_TYPE_ALL | WT_STAT_TYPE_CACHE_WALK | WT_STAT_TYPE_FAST | WT_STAT_TYPE_TREE_WALK); ++set; } WT_RET_NOTFOUND_OK(ret); if (set > 1) WT_RET_MSG(session, EINVAL, "Only one of all, fast, none configuration values should " "be specified"); /* * Now that we've parsed general statistics categories, process * sub-categories. */ if ((ret = __wt_config_subgets( session, &cval, "cache_walk", &sval)) == 0 && sval.val != 0) /* * Configuring cache walk statistics implies fast statistics. * Keep that knowledge internal for now - it may change in the * future. */ LF_SET(WT_STAT_TYPE_FAST | WT_STAT_TYPE_CACHE_WALK); WT_RET_NOTFOUND_OK(ret); if ((ret = __wt_config_subgets( session, &cval, "tree_walk", &sval)) == 0 && sval.val != 0) /* * Configuring tree walk statistics implies fast statistics. * Keep that knowledge internal for now - it may change in the * future. */ LF_SET(WT_STAT_TYPE_FAST | WT_STAT_TYPE_TREE_WALK); WT_RET_NOTFOUND_OK(ret); if ((ret = __wt_config_subgets( session, &cval, "clear", &sval)) == 0 && sval.val != 0) { if (!LF_ISSET(WT_STAT_TYPE_ALL | WT_STAT_TYPE_CACHE_WALK | WT_STAT_TYPE_FAST | WT_STAT_TYPE_TREE_WALK)) WT_RET_MSG(session, EINVAL, "the value \"clear\" can only be specified if " "statistics are enabled"); LF_SET(WT_STAT_CLEAR); } WT_RET_NOTFOUND_OK(ret); /* Configuring statistics clears any existing values. */ conn->stat_flags = flags; return (0); }