/* * __backup_uri -- * Backup a list of objects. */ static int __backup_uri(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *cfg[], bool *foundp, bool *log_only) { WT_CONFIG targetconf; WT_CONFIG_ITEM cval, k, v; WT_DECL_ITEM(tmp); WT_DECL_RET; bool target_list; const char *uri; *foundp = *log_only = false; /* * If we find a non-empty target configuration string, we have a job, * otherwise it's not our problem. */ WT_RET(__wt_config_gets(session, cfg, "target", &cval)); WT_RET(__wt_config_subinit(session, &targetconf, &cval)); for (cb->list_next = 0, target_list = false; (ret = __wt_config_next(&targetconf, &k, &v)) == 0; target_list = true) { /* If it is our first time through, allocate. */ if (!target_list) { *foundp = true; WT_ERR(__wt_scr_alloc(session, 512, &tmp)); } WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", (int)k.len, k.str)); uri = tmp->data; if (v.len != 0) WT_ERR_MSG(session, EINVAL, "%s: invalid backup target: URIs may need quoting", uri); /* * Handle log targets. We do not need to go through the * schema worker, just call the function to append them. * Set log_only only if it is our only URI target. */ if (WT_PREFIX_MATCH(uri, "log:")) { *log_only = !target_list; WT_ERR(__wt_backup_list_uri_append(session, uri, NULL)); } else WT_ERR(__wt_schema_worker(session, uri, NULL, __wt_backup_list_uri_append, cfg, 0)); } WT_ERR_NOTFOUND_OK(ret); err: __wt_scr_free(session, &tmp); return (ret); }
/* * __verify_config -- * Verification supports dumping pages in various formats. */ static int __verify_config(WT_SESSION_IMPL *session, const char *cfg[], WT_VSTUFF *vs) { WT_CONFIG_ITEM cval; WT_DECL_RET; ret = __wt_config_gets(session, cfg, "dump_address", &cval); if (ret != 0 && ret != WT_NOTFOUND) WT_RET(ret); if (ret == 0 && cval.val != 0) vs->dump_address = 1; ret = __wt_config_gets(session, cfg, "dump_blocks", &cval); if (ret != 0 && ret != WT_NOTFOUND) WT_RET(ret); if (ret == 0 && cval.val != 0) vs->dump_blocks = 1; ret = __wt_config_gets(session, cfg, "dump_pages", &cval); if (ret != 0 && ret != WT_NOTFOUND) WT_RET(ret); if (ret == 0 && cval.val != 0) vs->dump_pages = 1; #ifdef HAVE_DIAGNOSTIC /* * We use the verification code to do debugging dumps because if we're * dumping in debugging mode, we want to confirm the page is OK before * walking it. */ #else if (vs->dump_address || vs->dump_blocks || vs->dump_pages) WT_RET_MSG(session, ENOTSUP, "the WiredTiger library was not built in diagnostic mode"); #endif return (0); }
/* * __ckpt_server_config -- * Parse and setup the checkpoint server options. */ static int __ckpt_server_config(WT_SESSION_IMPL *session, const char **cfg, int *startp) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; WT_DECL_ITEM(tmp); WT_DECL_RET; conn = S2C(session); /* * The checkpoint configuration requires a wait time -- if it's not set, * we're not running at all. */ WT_RET(__wt_config_gets(session, cfg, "checkpoint.wait", &cval)); if (cval.val == 0) { *startp = 0; return (0); } conn->ckpt_usecs = (long)cval.val * 1000000; *startp = 1; WT_RET(__wt_config_gets(session, cfg, "checkpoint.name", &cval)); if (!WT_STRING_MATCH(WT_CHECKPOINT, cval.str, cval.len)) { WT_RET(__wt_scr_alloc(session, cval.len + 20, &tmp)); strcpy((char *)tmp->data, "name="); strncat((char *)tmp->data, cval.str, cval.len); ret = __wt_strndup(session, tmp->data, strlen("name=") + cval.len, &conn->ckpt_config); __wt_scr_free(&tmp); WT_RET(ret); } return (0); }
/* * __logmgr_sync_cfg -- * Interpret the transaction_sync config. */ static int __logmgr_sync_cfg(WT_SESSION_IMPL *session, const char **cfg) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; conn = S2C(session); WT_RET( __wt_config_gets(session, cfg, "transaction_sync.enabled", &cval)); if (cval.val) FLD_SET(conn->txn_logsync, WT_LOG_FLUSH); else FLD_CLR(conn->txn_logsync, WT_LOG_FLUSH); WT_RET( __wt_config_gets(session, cfg, "transaction_sync.method", &cval)); FLD_CLR(conn->txn_logsync, WT_LOG_DSYNC | WT_LOG_FSYNC); if (WT_STRING_MATCH("dsync", cval.str, cval.len)) FLD_SET(conn->txn_logsync, WT_LOG_DSYNC); else if (WT_STRING_MATCH("fsync", cval.str, cval.len)) FLD_SET(conn->txn_logsync, WT_LOG_FSYNC); return (0); }
/* * __verify_config -- * Debugging: verification supports dumping pages in various formats. */ static int __verify_config(WT_SESSION_IMPL *session, const char *cfg[], WT_VSTUFF *vs) { WT_CONFIG_ITEM cval; WT_RET(__wt_config_gets(session, cfg, "dump_address", &cval)); vs->dump_address = cval.val != 0; WT_RET(__wt_config_gets(session, cfg, "dump_blocks", &cval)); vs->dump_blocks = cval.val != 0; WT_RET(__wt_config_gets(session, cfg, "dump_pages", &cval)); vs->dump_pages = cval.val != 0; WT_RET(__wt_config_gets(session, cfg, "dump_shape", &cval)); vs->dump_shape = cval.val != 0; #if !defined(HAVE_DIAGNOSTIC) if (vs->dump_blocks || vs->dump_pages) WT_RET_MSG(session, ENOTSUP, "the WiredTiger library was not built in diagnostic mode"); #endif return (0); }
/*从cfg_arg中读取key对应的value值,并用cval返回*/ int __wt_ext_config_get(WT_EXTENSION_API *wt_api, WT_SESSION *wt_session, WT_CONFIG_ARG *cfg_arg, const char *key, WT_CONFIG_ITEM *cval) { WT_CONNECTION_IMPL *conn; WT_SESSION_IMPL *session; const char **cfg; conn = (WT_CONNECTION_IMPL *)wt_api->conn; if ((session = (WT_SESSION_IMPL *)wt_session) == NULL) session = conn->default_session; if ((cfg = (const char **)cfg_arg) == NULL) return WT_NOTFOUND; return __wt_config_gets(session, cfg, key, cval); }
/* * __conn_load_extension -- * WT_CONNECTION->load_extension method. */ static int __conn_load_extension( WT_CONNECTION *wt_conn, const char *path, const char *config) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; WT_DLH *dlh; WT_SESSION_IMPL *session; int (*entry)(WT_SESSION *, WT_EXTENSION_API *, const char *); const char *entry_name; dlh = NULL; conn = (WT_CONNECTION_IMPL *)wt_conn; CONNECTION_API_CALL(conn, session, load_extension, config, cfg); entry_name = NULL; WT_ERR(__wt_config_gets(session, cfg, "entry", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &entry_name)); /* * This assumes the underlying shared libraries are reference counted, * that is, that re-opening a shared library simply increments a ref * count, and closing it simply decrements the ref count, and the last * close discards the reference entirely -- in other words, we do not * check to see if we've already opened this shared library. */ WT_ERR(__wt_dlopen(session, path, &dlh)); WT_ERR(__wt_dlsym(session, dlh, entry_name, &entry)); /* Call the entry function. */ WT_ERR(entry(&session->iface, &__api, config)); /* Link onto the environment's list of open libraries. */ __wt_spin_lock(session, &conn->api_lock); TAILQ_INSERT_TAIL(&conn->dlhqh, dlh, q); __wt_spin_unlock(session, &conn->api_lock); if (0) { err: if (dlh != NULL) WT_TRET(__wt_dlclose(session, dlh)); } __wt_free(session, entry_name); API_END_NOTFOUND_MAP(session, ret); }
/* * __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_conn_compat_config -- * Configure compatibility version. */ int __wt_conn_compat_config(WT_SESSION_IMPL *session, const char **cfg) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; uint16_t patch; bool txn_active; conn = S2C(session); WT_RET(__wt_config_gets(session, cfg, "compatibility.release", &cval)); if (cval.len == 0) { conn->compat_major = WIREDTIGER_VERSION_MAJOR; conn->compat_minor = WIREDTIGER_VERSION_MINOR; return (0); } /* * Accept either a major.minor release string or a major.minor.patch * release string. We ignore the patch value, but allow it in the * string. */ if (sscanf(cval.str, "%" SCNu16 ".%" SCNu16, &conn->compat_major, &conn->compat_minor) != 2 && sscanf(cval.str, "%" SCNu16 ".%" SCNu16 ".%" SCNu16, &conn->compat_major, &conn->compat_minor, &patch) != 3) WT_RET_MSG(session, EINVAL, "illegal compatibility release"); if (conn->compat_major > WIREDTIGER_VERSION_MAJOR) WT_RET_MSG(session, ENOTSUP, "unsupported major version"); if (conn->compat_major == WIREDTIGER_VERSION_MAJOR && conn->compat_minor > WIREDTIGER_VERSION_MINOR) WT_RET_MSG(session, ENOTSUP, "unsupported minor version"); /* * We're doing an upgrade or downgrade, check whether transactions are * active. */ WT_RET(__wt_txn_activity_check(session, &txn_active)); if (txn_active) WT_RET_MSG(session, ENOTSUP, "system must be quiescent for upgrade or downgrade"); return (0); }
/* * __backup_uri -- * Backup a list of objects. */ static int __backup_uri(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *cfg[], int *foundp) { WT_CONFIG targetconf; WT_CONFIG_ITEM cval, k, v; WT_DECL_ITEM(tmp); WT_DECL_RET; int target_list; const char *uri; *foundp = target_list = 0; /* * If we find a non-empty target configuration string, we have a job, * otherwise it's not our problem. */ WT_RET(__wt_config_gets(session, cfg, "target", &cval)); WT_RET(__wt_config_subinit(session, &targetconf, &cval)); for (cb->list_next = 0; (ret = __wt_config_next(&targetconf, &k, &v)) == 0;) { if (!target_list) { target_list = *foundp = 1; WT_ERR(__wt_scr_alloc(session, 512, &tmp)); } WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", (int)k.len, k.str)); uri = tmp->data; if (v.len != 0) WT_ERR_MSG(session, EINVAL, "%s: invalid backup target: URIs may need quoting", uri); WT_ERR(__wt_schema_worker( session, uri, NULL, __wt_backup_list_uri_append, cfg, 0)); } WT_ERR_NOTFOUND_OK(ret); err: __wt_scr_free(&tmp); return (ret); }
/* * __conn_reconfigure -- * WT_CONNECTION->reconfigure method. */ static int __conn_reconfigure(WT_CONNECTION *wt_conn, const char *config) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; WT_SESSION_IMPL *session; /* * Special version of cfg that doesn't include the default config: used * to limit changes to values that the application sets explicitly. * Note that any function using this value has to be prepared to handle * not-found as a valid option return. */ const char *raw_cfg[] = { config, NULL }; conn = (WT_CONNECTION_IMPL *)wt_conn; CONNECTION_API_CALL(conn, session, reconfigure, config, cfg); /* Turning on statistics clears any existing values. */ if ((ret = __wt_config_gets(session, raw_cfg, "statistics", &cval)) == 0) { conn->statistics = cval.val == 0 ? 0 : 1; if (conn->statistics) __wt_stat_clear_connection_stats(&conn->stats); } WT_ERR_NOTFOUND_OK(ret); WT_ERR(__wt_conn_cache_pool_config(session, cfg)); WT_ERR(__wt_cache_config(conn, raw_cfg)); WT_ERR(__conn_verbose_config(session, raw_cfg)); /* Wake up the cache pool server so any changes are noticed. */ if (F_ISSET(conn, WT_CONN_CACHE_POOL)) WT_ERR(__wt_cond_signal( session, __wt_process.cache_pool->cache_pool_cond)); err: API_END(session); return (ret); }
/* * __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); }
/*读取evict LRU cache的配置*/ static int __cache_config_local(WT_SESSION_IMPL* session, int shared, const char* cfg[]) { WT_CACHE* cache; WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; uint32_t evict_workers_max, evict_workers_min; conn = S2C(session); cache = conn->cache; /*读取cache size*/ if (!shared){ WT_RET(__wt_config_gets(session, cfg, "cache_size", &cval)); conn->cache_size = (uint64_t)cval.val; } WT_RET(__wt_config_gets(session, cfg, "cache_overhead", &cval)); cache->overhead_pct = (u_int)cval.val; WT_RET(__wt_config_gets(session, cfg, "eviction_target", &cval)); cache->eviction_target = (u_int)cval.val; WT_RET(__wt_config_gets(session, cfg, "eviction_trigger", &cval)); cache->eviction_trigger = (u_int)cval.val; WT_RET(__wt_config_gets(session, cfg, "eviction_dirty_target", &cval)); cache->eviction_dirty_target = (u_int)cval.val; WT_RET(__wt_config_gets(session, cfg, "eviction.threads_max", &cval)); WT_ASSERT(session, cval.val > 0); evict_workers_max = (uint32_t)cval.val - 1; WT_RET(__wt_config_gets(session, cfg, "eviction.threads_min", &cval)); WT_ASSERT(session, cval.val > 0); evict_workers_min = (uint32_t)cval.val - 1; /*合法性校验*/ if (evict_workers_min > evict_workers_max) WT_RET_MSG(session, EINVAL, "eviction=(threads_min) cannot be greater than eviction=(threads_max)"); conn->evict_workers_max = evict_workers_max; conn->evict_workers_min = evict_workers_min; return 0; }
/* * __wt_snapshot -- * Snapshot the tree. */ int __wt_snapshot(WT_SESSION_IMPL *session, const char *cfg[]) { WT_CONFIG_ITEM cval; WT_DECL_RET; char *name; name = NULL; /* This may be a named snapshot, check the configuration. */ if ((ret = __wt_config_gets( session, cfg, "snapshot", &cval)) != 0 && ret != WT_NOTFOUND) WT_RET(ret); if (cval.len != 0) WT_RET(__wt_strndup(session, cval.str, cval.len, &name)); ret = __snapshot_worker(session, name, 0, SNAPSHOT); __wt_free(session, name); return (ret); }
/* * __lsm_tree_open_check -- * Validate the configuration of an LSM tree. */ static int __lsm_tree_open_check(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree) { WT_CONFIG_ITEM cval; uint64_t maxleafpage, required; const char *cfg[] = { WT_CONFIG_BASE( session, session_create), lsm_tree->file_config, NULL }; WT_RET(__wt_config_gets(session, cfg, "leaf_page_max", &cval)); maxleafpage = (uint64_t)cval.val; /* Three chunks, plus one page for each participant in a merge. */ required = 3 * lsm_tree->chunk_size + lsm_tree->merge_threads * (lsm_tree->merge_max * maxleafpage); if (S2C(session)->cache_size < required) WT_RET_MSG(session, EINVAL, "The LSM configuration requires a cache size of at least %" PRIu64 ". Configured size is %" PRIu64, required, S2C(session)->cache_size); return (0); }
/* * __drop_file -- * Drop a file. */ static int __drop_file( WT_SESSION_IMPL *session, const char *uri, int force, const char *cfg[]) { WT_CONFIG_ITEM cval; WT_DECL_RET; int exist, remove_files; const char *filename; WT_RET(__wt_config_gets(session, cfg, "remove_files", &cval)); remove_files = (cval.val != 0); filename = uri; if (!WT_PREFIX_SKIP(filename, "file:")) return (EINVAL); /* Close all btree handles associated with this file. */ WT_WITH_DHANDLE_LOCK(session, ret = __wt_conn_dhandle_close_all(session, uri, force)); WT_RET(ret); /* Remove the metadata entry (ignore missing items). */ WT_TRET(__wt_metadata_remove(session, uri)); if (!remove_files) return (ret); /* Remove the underlying physical file. */ exist = 0; WT_TRET(__wt_exist(session, filename, &exist)); if (exist) { /* * There is no point tracking this operation: there is no going * back from here. */ WT_TRET(__wt_remove(session, filename)); } return (ret); }
/* * __conn_config_env -- * Read configuration from an environment variable, if set. */ static int __conn_config_env(WT_SESSION_IMPL *session, const char **cfg) { WT_CONFIG_ITEM cval; const char *env_config; if ((env_config = getenv("WIREDTIGER_CONFIG")) == NULL || strlen(env_config) == 0) return (0); /* * Security stuff: * * If the "use_environment_priv" configuration string is set, use the * environment variable if the process has appropriate privileges. */ WT_RET(__wt_config_gets(session, cfg, "use_environment_priv", &cval)); if (cval.val == 0 && __wt_has_priv()) WT_RET_MSG(session, WT_ERROR, "%s", "WIREDTIGER_CONFIG environment variable set but process " "lacks privileges to use that environment variable"); /* Check the configuration string. */ WT_RET(__wt_config_check( session, __wt_confchk_wiredtiger_open, env_config, 0)); /* * The environment setting comes second-to-last: it overrides the * WiredTiger.config file (if any), but not the config passed by the * application. */ while (cfg[1] != NULL) ++cfg; cfg[1] = cfg[0]; cfg[0] = env_config; 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 */ }
int __wt_schema_drop(WT_SESSION_IMPL *session, const char *uri, const char *cfg[]) { WT_CONFIG_ITEM cval; int force, ret; cval.val = 0; ret = __wt_config_gets(session, cfg, "force", &cval); if (ret != 0 && ret != WT_NOTFOUND) WT_RET(ret); force = cval.val == 0 ? 0 : 1; /* Disallow drops from the WiredTiger name space. */ WT_RET(__wt_schema_name_check(session, uri)); if (WT_PREFIX_MATCH(uri, "colgroup:")) ret = __drop_colgroup(session, uri, force, cfg); else if (WT_PREFIX_MATCH(uri, "file:")) ret = __drop_file(session, uri, force); else if (WT_PREFIX_MATCH(uri, "index:")) ret = __drop_index(session, uri, force, cfg); else if (WT_PREFIX_MATCH(uri, "table:")) ret = __drop_table(session, uri, force); else return (__wt_unknown_object_type(session, uri)); /* * Map WT_NOTFOUND to ENOENT (or to 0 if "force" is set), based on the * assumption WT_NOTFOUND means there was no schema file entry. The * underlying drop functions should handle this case (we passed them * the "force" value), but better safe than sorry. */ if (ret == WT_NOTFOUND) ret = force ? 0 : ENOENT; 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); }
/* * __wt_conn_compat_config -- * Configure compatibility version. */ int __wt_conn_compat_config( WT_SESSION_IMPL *session, const char **cfg, bool reconfig) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; uint16_t max_major, max_minor, min_major, min_minor; uint16_t rel_major, rel_minor; char *value; bool txn_active; conn = S2C(session); value = NULL; max_major = WT_CONN_COMPAT_NONE; max_minor = WT_CONN_COMPAT_NONE; min_major = WT_CONN_COMPAT_NONE; min_minor = WT_CONN_COMPAT_NONE; WT_RET(__wt_config_gets(session, cfg, "compatibility.release", &cval)); if (cval.len == 0) { rel_major = WIREDTIGER_VERSION_MAJOR; rel_minor = WIREDTIGER_VERSION_MINOR; F_CLR(conn, WT_CONN_COMPATIBILITY); } else { WT_RET(__conn_compat_parse( session, &cval, &rel_major, &rel_minor)); /* * We're doing an upgrade or downgrade, check whether * transactions are active. */ WT_RET(__wt_txn_activity_check(session, &txn_active)); if (txn_active) WT_RET_MSG(session, ENOTSUP, "system must be quiescent" " for upgrade or downgrade"); F_SET(conn, WT_CONN_COMPATIBILITY); } /* * If we're a reconfigure and the user did not set any compatibility, * we're done. */ if (reconfig && !F_ISSET(conn, WT_CONN_COMPATIBILITY)) goto done; /* * The maximum and minimum required version for existing files * is only available on opening the connection, not reconfigure. */ WT_RET(__wt_config_gets(session, cfg, "compatibility.require_min", &cval)); if (cval.len != 0) WT_RET(__conn_compat_parse( session, &cval, &min_major, &min_minor)); WT_RET(__wt_config_gets(session, cfg, "compatibility.require_max", &cval)); if (cval.len != 0) WT_RET(__conn_compat_parse( session, &cval, &max_major, &max_minor)); /* * The maximum required must be greater than or equal to the * compatibility release we're using now. This is on an open and we're * checking the two against each other. We'll check against what was * saved on a restart later. */ if (!reconfig && max_major != WT_CONN_COMPAT_NONE && (max_major < rel_major || (max_major == rel_major && max_minor < rel_minor))) WT_RET_MSG(session, ENOTSUP, WT_COMPAT_MSG_PREFIX "required max of %" PRIu16 ".%" PRIu16 "cannot be smaller than compatibility release %" PRIu16 ".%" PRIu16, max_major, max_minor, rel_major, rel_minor); /* * The minimum required must be less than or equal to the compatibility * release we're using now. This is on an open and we're checking the * two against each other. We'll check against what was saved on a * restart later. */ if (!reconfig && min_major != WT_CONN_COMPAT_NONE && (min_major > rel_major || (min_major == rel_major && min_minor > rel_minor))) WT_RET_MSG(session, ENOTSUP, WT_COMPAT_MSG_PREFIX "required min of %" PRIu16 ".%" PRIu16 "cannot be larger than compatibility release %" PRIu16 ".%" PRIu16, min_major, min_minor, rel_major, rel_minor); /* * On a reconfigure, check the new release version against any * required maximum version set on open. */ if (reconfig && conn->req_max_major != WT_CONN_COMPAT_NONE && (conn->req_max_major < rel_major || (conn->req_max_major == rel_major && conn->req_max_minor < rel_minor))) WT_RET_MSG(session, ENOTSUP, WT_COMPAT_MSG_PREFIX "required max of %" PRIu16 ".%" PRIu16 "cannot be smaller than requested compatibility release %" PRIu16 ".%" PRIu16, conn->req_max_major, conn->req_max_minor, rel_major, rel_minor); /* * On a reconfigure, check the new release version against any * required minimum version set on open. */ if (reconfig && conn->req_min_major != WT_CONN_COMPAT_NONE && (conn->req_min_major > rel_major || (conn->req_min_major == rel_major && conn->req_min_minor > rel_minor))) WT_RET_MSG(session, ENOTSUP, WT_COMPAT_MSG_PREFIX "required min of %" PRIu16 ".%" PRIu16 "cannot be larger than requested compatibility release %" PRIu16 ".%" PRIu16, conn->req_min_major, conn->req_min_minor, rel_major, rel_minor); conn->compat_major = rel_major; conn->compat_minor = rel_minor; /* * Only rewrite the turtle file if this is a reconfig. On startup * it will get written as part of creating the connection. We do this * after checking the required minimum version so that we don't rewrite * the turtle file if there is an error. */ if (reconfig) WT_RET(__wt_metadata_turtle_rewrite(session)); /* * The required maximum and minimum cannot be set via reconfigure and * they are meaningless on a newly created database. We're done in * those cases. */ if (reconfig || conn->is_new || (min_major == WT_CONN_COMPAT_NONE && max_major == WT_CONN_COMPAT_NONE)) goto done; /* * Check the minimum required against any saved compatibility version * in the turtle file saved from an earlier run. */ rel_major = rel_minor = WT_CONN_COMPAT_NONE; if ((ret = __wt_metadata_search(session, WT_METADATA_COMPAT, &value)) == 0) { WT_ERR(__wt_config_getones(session, value, "major", &cval)); rel_major = (uint16_t)cval.val; WT_ERR(__wt_config_getones(session, value, "minor", &cval)); rel_minor = (uint16_t)cval.val; if (max_major != WT_CONN_COMPAT_NONE && (max_major < rel_major || (max_major == rel_major && max_minor < rel_minor))) WT_ERR_MSG(session, ENOTSUP, WT_COMPAT_MSG_PREFIX "required max of %" PRIu16 ".%" PRIu16 "cannot be larger than saved release %" PRIu16 ".%" PRIu16, max_major, max_minor, rel_major, rel_minor); if (min_major != WT_CONN_COMPAT_NONE && (min_major > rel_major || (min_major == rel_major && min_minor > rel_minor))) WT_ERR_MSG(session, ENOTSUP, WT_COMPAT_MSG_PREFIX "required min of %" PRIu16 ".%" PRIu16 "cannot be larger than saved release %" PRIu16 ".%" PRIu16, min_major, min_minor, rel_major, rel_minor); } else if (ret == WT_NOTFOUND) ret = 0; else WT_ERR(ret); done: conn->req_max_major = max_major; conn->req_max_minor = max_minor; conn->req_min_major = min_major; conn->req_min_minor = min_minor; err: __wt_free(session, value); return (ret); }
/* * wiredtiger_open -- * Main library entry point: open a new connection to a WiredTiger * database. */ int wiredtiger_open(const char *home, WT_EVENT_HANDLER *event_handler, const char *config, WT_CONNECTION **wt_connp) { static WT_CONNECTION stdc = { __conn_close, __conn_reconfigure, __conn_get_home, __conn_is_new, __conn_open_session, __conn_load_extension, __conn_add_data_source, __conn_add_collator, __conn_add_compressor, __conn_add_extractor }; static struct { const char *name; uint32_t flag; } *ft, directio_types[] = { { "data", WT_DIRECTIO_DATA }, { "log", WT_DIRECTIO_LOG }, { NULL, 0 } }; WT_CONFIG subconfig; WT_CONFIG_ITEM cval, skey, sval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; WT_ITEM *cbuf, expath, exconfig; WT_SESSION_IMPL *session; const char *cfg[] = { __wt_confdfl_wiredtiger_open, config, NULL, NULL, NULL }; int exist; *wt_connp = NULL; session = NULL; cbuf = NULL; WT_CLEAR(expath); WT_CLEAR(exconfig); WT_RET(__wt_library_init()); WT_RET(__wt_calloc_def(NULL, 1, &conn)); conn->iface = stdc; /* * Immediately link the structure into the connection structure list: * the only thing ever looked at on that list is the database name, * and a NULL value is fine. */ __wt_spin_lock(NULL, &__wt_process.spinlock); TAILQ_INSERT_TAIL(&__wt_process.connqh, conn, q); __wt_spin_unlock(NULL, &__wt_process.spinlock); session = conn->default_session = &conn->dummy_session; session->iface.connection = &conn->iface; session->name = "wiredtiger_open"; __wt_event_handler_set(session, event_handler); /* Remaining basic initialization of the connection structure. */ WT_ERR(__wt_connection_init(conn)); /* Check the configuration strings. */ WT_ERR(__wt_config_check( session, __wt_confchk_wiredtiger_open, config, 0)); /* Get the database home. */ WT_ERR(__conn_home(session, home, cfg)); /* Make sure no other thread of control already owns this database. */ WT_ERR(__conn_single(session, cfg)); /* Read the database-home configuration file. */ WT_ERR(__conn_config_file(session, cfg, &cbuf)); /* Read the environment variable configuration. */ WT_ERR(__conn_config_env(session, cfg)); WT_ERR(__wt_config_gets(session, cfg, "hazard_max", &cval)); conn->hazard_max = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "session_max", &cval)); conn->session_size = (uint32_t)cval.val + WT_NUM_INTERNAL_SESSIONS; WT_ERR(__wt_config_gets(session, cfg, "lsm_merge", &cval)); if (cval.val) F_SET(conn, WT_CONN_LSM_MERGE); WT_ERR(__wt_config_gets(session, cfg, "sync", &cval)); if (cval.val) F_SET(conn, WT_CONN_SYNC); WT_ERR(__wt_config_gets(session, cfg, "transactional", &cval)); if (cval.val) F_SET(conn, WT_CONN_TRANSACTIONAL); /* Configure verbose flags. */ WT_ERR(__conn_verbose_config(session, cfg)); WT_ERR(__wt_conn_cache_pool_config(session, cfg)); WT_ERR(__wt_config_gets(session, cfg, "logging", &cval)); if (cval.val != 0) WT_ERR(__wt_open( session, WT_LOG_FILENAME, 1, 0, 0, &conn->log_fh)); /* Configure direct I/O and buffer alignment. */ WT_ERR(__wt_config_gets(session, cfg, "buffer_alignment", &cval)); if (cval.val == -1) conn->buffer_alignment = WT_BUFFER_ALIGNMENT_DEFAULT; else conn->buffer_alignment = (size_t)cval.val; #ifndef HAVE_POSIX_MEMALIGN if (conn->buffer_alignment != 0) WT_ERR_MSG(session, EINVAL, "buffer_alignment requires posix_memalign"); #endif /* * Configuration: direct_io, mmap, statistics. */ WT_ERR(__wt_config_gets(session, cfg, "direct_io", &cval)); for (ft = directio_types; ft->name != NULL; ft++) { ret = __wt_config_subgets(session, &cval, ft->name, &sval); if (ret == 0) { if (sval.val) FLD_SET(conn->direct_io, ft->flag); } else if (ret != WT_NOTFOUND) goto err; } WT_ERR(__wt_config_gets(session, cfg, "mmap", &cval)); conn->mmap = cval.val == 0 ? 0 : 1; WT_ERR(__wt_config_gets(session, cfg, "statistics", &cval)); conn->statistics = cval.val == 0 ? 0 : 1; /* Load any extensions referenced in the config. */ WT_ERR(__wt_config_gets(session, cfg, "extensions", &cval)); WT_ERR(__wt_config_subinit(session, &subconfig, &cval)); while ((ret = __wt_config_next(&subconfig, &skey, &sval)) == 0) { WT_ERR(__wt_buf_fmt( session, &expath, "%.*s", (int)skey.len, skey.str)); if (sval.len > 0) WT_ERR(__wt_buf_fmt(session, &exconfig, "entry=%.*s\n", (int)sval.len, sval.str)); WT_ERR(conn->iface.load_extension(&conn->iface, expath.data, (sval.len > 0) ? exconfig.data : NULL)); } WT_ERR_NOTFOUND_OK(ret); /* * Open the connection; if that fails, the connection handle has been * destroyed by the time the open function returns. */ if ((ret = __wt_connection_open(conn, cfg)) != 0) { conn = NULL; WT_ERR(ret); } /* Open the default session. */ WT_ERR(__wt_open_session(conn, 1, NULL, NULL, &conn->default_session)); session = conn->default_session; /* * Check on the turtle and metadata files, creating them if necessary * (which avoids application threads racing to create the metadata file * later). */ WT_ERR(__wt_meta_turtle_init(session, &exist)); if (!exist) { /* * We're single-threaded, but acquire the schema lock * regardless: the lower level code checks that it is * appropriately synchronized. */ WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_create(session, WT_METADATA_URI, NULL)); WT_ERR(ret); } WT_ERR(__wt_metadata_open(session)); /* If there's a hot-backup file, load it. */ WT_ERR(__wt_metadata_load_backup(session)); /* * XXX LSM initialization. * This is structured so that it could be moved to an extension. */ WT_ERR(__wt_lsm_init(&conn->iface, NULL)); STATIC_ASSERT(offsetof(WT_CONNECTION_IMPL, iface) == 0); *wt_connp = &conn->iface; /* * Destroying the connection on error will destroy our session handle, * cleanup using the session handle first, then discard the connection. */ err: if (cbuf != NULL) __wt_buf_free(session, cbuf); __wt_buf_free(session, &expath); __wt_buf_free(session, &exconfig); if (ret != 0 && conn != NULL) WT_TRET(__wt_connection_destroy(conn)); /* Let the server threads proceed. */ if (ret == 0) conn->connection_initialized = 1; return (ret); }
/* * __logmgr_config -- * Parse and setup the logging server options. */ static int __logmgr_config( WT_SESSION_IMPL *session, const char **cfg, bool *runp, bool reconfig) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; bool enabled; conn = S2C(session); WT_RET(__wt_config_gets(session, cfg, "log.enabled", &cval)); enabled = cval.val != 0; /* * If we're reconfiguring, enabled must match the already * existing setting. * * If it is off and the user it turning it on, or it is on * and the user is turning it off, return an error. */ if (reconfig && ((enabled && !FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED)) || (!enabled && FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED)))) return (EINVAL); /* Logging is incompatible with in-memory */ if (enabled) { WT_RET(__wt_config_gets(session, cfg, "in_memory", &cval)); if (cval.val != 0) WT_RET_MSG(session, EINVAL, "In memory configuration incompatible with " "log=(enabled=true)"); } *runp = enabled; /* * Setup a log path and compression even if logging is disabled in case * we are going to print a log. Only do this on creation. Once a * compressor or log path are set they cannot be changed. */ if (!reconfig) { conn->log_compressor = NULL; WT_RET(__wt_config_gets_none( session, cfg, "log.compressor", &cval)); WT_RET(__wt_compressor_config( session, &cval, &conn->log_compressor)); WT_RET(__wt_config_gets(session, cfg, "log.path", &cval)); WT_RET(__wt_strndup( session, cval.str, cval.len, &conn->log_path)); } /* We are done if logging isn't enabled. */ if (!*runp) return (0); WT_RET(__wt_config_gets(session, cfg, "log.archive", &cval)); if (cval.val != 0) FLD_SET(conn->log_flags, WT_CONN_LOG_ARCHIVE); if (!reconfig) { /* * Ignore if the user tries to change the file size. The * amount of memory allocated to the log slots may be based * on the log file size at creation and we don't want to * re-allocate that memory while running. */ WT_RET(__wt_config_gets(session, cfg, "log.file_max", &cval)); conn->log_file_max = (wt_off_t)cval.val; WT_STAT_FAST_CONN_SET(session, log_max_filesize, conn->log_file_max); } /* * If pre-allocation is configured, set the initial number to a few. * We'll adapt as load dictates. */ WT_RET(__wt_config_gets(session, cfg, "log.prealloc", &cval)); if (cval.val != 0) conn->log_prealloc = 1; /* * Note that it is meaningless to reconfigure this value during * runtime. It only matters on create before recovery runs. */ WT_RET(__wt_config_gets_def(session, cfg, "log.recover", 0, &cval)); if (cval.len != 0 && WT_STRING_MATCH("error", cval.str, cval.len)) FLD_SET(conn->log_flags, WT_CONN_LOG_RECOVER_ERR); WT_RET(__wt_config_gets(session, cfg, "log.zero_fill", &cval)); if (cval.val != 0) { if (F_ISSET(conn, WT_CONN_READONLY)) WT_RET_MSG(session, EINVAL, "Read-only configuration incompatible with " "zero-filling log files"); FLD_SET(conn->log_flags, WT_CONN_LOG_ZERO_FILL); } WT_RET(__logmgr_sync_cfg(session, cfg)); if (conn->log_cond != NULL) WT_RET(__wt_cond_auto_signal(session, conn->log_cond)); return (0); }
/* * __conn_single -- * Confirm that no other thread of control is using this database. */ static int __conn_single(WT_SESSION_IMPL *session, const char **cfg) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn, *t; WT_DECL_RET; off_t size; uint32_t len; int created; char buf[256]; conn = S2C(session); /* * Optionally create the wiredtiger flag file if it doesn't already * exist. We don't actually care if we create it or not, the "am I the * only locker" tests are all that matter. */ WT_RET(__wt_config_gets(session, cfg, "create", &cval)); WT_RET(__wt_open(session, WT_SINGLETHREAD, cval.val == 0 ? 0 : 1, 0, 0, &conn->lock_fh)); /* * Lock a byte of the file: if we don't get the lock, some other process * is holding it, we're done. Note the file may be zero-length length, * and that's OK, the underlying call supports acquisition of locks past * the end-of-file. */ if (__wt_bytelock(conn->lock_fh, (off_t)0, 1) != 0) WT_ERR_MSG(session, EBUSY, "%s", "WiredTiger database is already being managed by another " "process"); /* Check to see if another thread of control has this database open. */ __wt_spin_lock(session, &__wt_process.spinlock); TAILQ_FOREACH(t, &__wt_process.connqh, q) if (t->home != NULL && t != conn && strcmp(t->home, conn->home) == 0) { ret = EBUSY; break; } __wt_spin_unlock(session, &__wt_process.spinlock); if (ret != 0) WT_ERR_MSG(session, EBUSY, "%s", "WiredTiger database is already being managed by another " "thread in this process"); /* * If the size of the file is 0, we created it (or we're racing with * the thread that created it, it doesn't matter), write some bytes * into the file. Strictly speaking, this isn't even necessary, but * zero-length files always make me nervous. */ WT_ERR(__wt_filesize(session, conn->lock_fh, &size)); if (size == 0) { len = (uint32_t)snprintf(buf, sizeof(buf), "%s\n%s\n", WT_SINGLETHREAD, wiredtiger_version(NULL, NULL, NULL)); WT_ERR(__wt_write( session, conn->lock_fh, (off_t)0, (uint32_t)len, buf)); created = 1; } else created = 0; /* * If we found a zero-length WiredTiger lock file, and eventually ended * as the database owner, return that we created the database. (There * is a theoretical chance that another process created the WiredTiger * lock file but we won the race to add the WT_CONNECTION_IMPL structure * to the process' list. It doesn't much matter, only one thread will * be told it created the database.) */ conn->is_new = created; return (0); err: if (conn->lock_fh != NULL) { WT_TRET(__wt_close(session, conn->lock_fh)); conn->lock_fh = NULL; } return (ret); }
/* * __wt_session_compact -- * WT_SESSION.compact method. */ int __wt_session_compact( WT_SESSION *wt_session, const char *uri, const char *config) { WT_COMPACT_STATE compact; WT_CONFIG_ITEM cval; WT_DATA_SOURCE *dsrc; WT_DECL_RET; WT_SESSION_IMPL *session; u_int i; bool ignore_cache_size_set; ignore_cache_size_set = false; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, compact, config, cfg); /* * The compaction thread should not block when the cache is full: it is * holding locks blocking checkpoints and once the cache is full, it can * spend a long time doing eviction. */ if (!F_ISSET(session, WT_SESSION_IGNORE_CACHE_SIZE)) { ignore_cache_size_set = true; F_SET(session, WT_SESSION_IGNORE_CACHE_SIZE); } /* In-memory ignores compaction operations. */ if (F_ISSET(S2C(session), WT_CONN_IN_MEMORY)) goto err; /* * Non-LSM object compaction requires checkpoints, which are impossible * in transactional contexts. Disallow in all contexts (there's no * reason for LSM to allow this, possible or not), and check now so the * error message isn't confusing. */ WT_ERR(__wt_txn_context_check(session, false)); /* Disallow objects in the WiredTiger name space. */ WT_ERR(__wt_str_name_check(session, uri)); if (!WT_PREFIX_MATCH(uri, "colgroup:") && !WT_PREFIX_MATCH(uri, "file:") && !WT_PREFIX_MATCH(uri, "index:") && !WT_PREFIX_MATCH(uri, "lsm:") && !WT_PREFIX_MATCH(uri, "table:")) { if ((dsrc = __wt_schema_get_source(session, uri)) != NULL) ret = dsrc->compact == NULL ? __wt_object_unsupported(session, uri) : dsrc->compact( dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg); else ret = __wt_bad_object_type(session, uri); goto err; } /* Setup the session handle's compaction state structure. */ memset(&compact, 0, sizeof(WT_COMPACT_STATE)); session->compact = &compact; /* Compaction can be time-limited. */ WT_ERR(__wt_config_gets(session, cfg, "timeout", &cval)); session->compact->max_time = (uint64_t)cval.val; __wt_epoch(session, &session->compact->begin); /* Find the types of data sources being compacted. */ WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_worker(session, uri, __compact_handle_append, __compact_uri_analyze, cfg, 0)); WT_ERR(ret); if (session->compact->lsm_count != 0) WT_ERR(__wt_schema_worker( session, uri, NULL, __wt_lsm_compact, cfg, 0)); if (session->compact->file_count != 0) WT_ERR(__compact_worker(session)); err: session->compact = NULL; for (i = 0; i < session->op_handle_next; ++i) { WT_WITH_DHANDLE(session, session->op_handle[i], WT_TRET(__compact_end(session))); WT_WITH_DHANDLE(session, session->op_handle[i], WT_TRET(__wt_session_release_dhandle(session))); } __wt_free(session, session->op_handle); session->op_handle_allocated = session->op_handle_next = 0; /* * Release common session resources (for example, checkpoint may acquire * significant reconciliation structures/memory). */ WT_TRET(__wt_session_release_resources(session)); if (ignore_cache_size_set) F_CLR(session, WT_SESSION_IGNORE_CACHE_SIZE); if (ret != 0) WT_STAT_CONN_INCR(session, session_table_compact_fail); else WT_STAT_CONN_INCR(session, session_table_compact_success); API_END_RET_NOTFOUND_MAP(session, 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 }; 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); }
/* * __wt_block_open -- * Open a block handle. */ int __wt_block_open(WT_SESSION_IMPL *session, const char *filename, const char *cfg[], int forced_salvage, int readonly, uint32_t allocsize, WT_BLOCK **blockp) { WT_BLOCK *block; WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; WT_TRET(__wt_verbose(session, WT_VERB_BLOCK, "open: %s", filename)); conn = S2C(session); *blockp = NULL; __wt_spin_lock(session, &conn->block_lock); TAILQ_FOREACH(block, &conn->blockqh, q) if (strcmp(filename, block->name) == 0) { ++block->ref; *blockp = block; __wt_spin_unlock(session, &conn->block_lock); return (0); } /* Basic structure allocation, initialization. */ WT_ERR(__wt_calloc_def(session, 1, &block)); block->ref = 1; TAILQ_INSERT_HEAD(&conn->blockqh, block, q); WT_ERR(__wt_strdup(session, filename, &block->name)); block->allocsize = allocsize; WT_ERR(__wt_config_gets(session, cfg, "block_allocation", &cval)); block->allocfirst = WT_STRING_MATCH("first", cval.str, cval.len) ? 1 : 0; /* Configuration: optional OS buffer cache maximum size. */ WT_ERR(__wt_config_gets(session, cfg, "os_cache_max", &cval)); block->os_cache_max = (size_t)cval.val; #ifdef HAVE_POSIX_FADVISE if (conn->direct_io && block->os_cache_max) WT_ERR_MSG(session, EINVAL, "os_cache_max not supported in combination with direct_io"); #else if (block->os_cache_max) WT_ERR_MSG(session, EINVAL, "os_cache_max not supported if posix_fadvise not " "available"); #endif /* Configuration: optional immediate write scheduling flag. */ WT_ERR(__wt_config_gets(session, cfg, "os_cache_dirty_max", &cval)); block->os_cache_dirty_max = (size_t)cval.val; #ifdef HAVE_SYNC_FILE_RANGE if (conn->direct_io && block->os_cache_dirty_max) WT_ERR_MSG(session, EINVAL, "os_cache_dirty_max not supported in combination with " "direct_io"); #else if (block->os_cache_dirty_max) { /* * Ignore any setting if it is not supported. */ block->os_cache_dirty_max = 0; WT_ERR(__wt_verbose(session, WT_VERB_BLOCK, "os_cache_dirty_max ignored when sync_file_range not " "available")); } #endif /* Open the underlying file handle. */ WT_ERR(__wt_open(session, filename, 0, 0, readonly ? WT_FILE_TYPE_CHECKPOINT : WT_FILE_TYPE_DATA, &block->fh)); /* Initialize the live checkpoint's lock. */ WT_ERR(__wt_spin_init(session, &block->live_lock, "block manager")); /* * Read the description information from the first block. * * Salvage is a special case: if we're forcing the salvage, we don't * look at anything, including the description information. */ if (!forced_salvage) WT_ERR(__desc_read(session, block)); *blockp = block; __wt_spin_unlock(session, &conn->block_lock); return (0); err: WT_TRET(__block_destroy(session, block)); __wt_spin_unlock(session, &conn->block_lock); return (ret); }
/* * __wt_conn_optrack_setup -- * Set up operation logging. */ int __wt_conn_optrack_setup(WT_SESSION_IMPL *session, const char *cfg[], bool reconfig) { WT_CONFIG_ITEM cval; WT_CONNECTION_IMPL *conn; WT_DECL_ITEM(buf); WT_DECL_RET; conn = S2C(session); /* Once an operation tracking path has been set it can't be changed. */ if (!reconfig) { WT_RET(__wt_config_gets(session, cfg, "operation_tracking.path", &cval)); WT_RET(__wt_strndup(session, cval.str, cval.len, &conn->optrack_path)); } WT_RET(__wt_config_gets(session, cfg, "operation_tracking.enabled", &cval)); if (cval.val == 0) { if (F_ISSET(conn, WT_CONN_OPTRACK)) { WT_RET(__wt_conn_optrack_teardown(session, reconfig)); F_CLR(conn, WT_CONN_OPTRACK); } return (0); } if (F_ISSET(conn, WT_CONN_READONLY)) /* Operation tracking isn't supported in read-only mode */ WT_RET_MSG(session, EINVAL, "Operation tracking is incompatible with read only " "configuration."); if (F_ISSET(conn, WT_CONN_OPTRACK)) /* Already enabled, nothing else to do */ return (0); /* * Operation tracking files will include the ID of the creating process * in their name, so we can distinguish between log files created by * different WiredTiger processes in the same directory. We cache the * process id for future use. */ conn->optrack_pid = __wt_process_id(); /* * Open the file in the same directory that will hold a map of * translations between function names and function IDs. If the file * exists, remove it. */ WT_RET(__wt_scr_alloc(session, 0, &buf)); WT_ERR(__wt_filename_construct(session, conn->optrack_path, "optrack-map", conn->optrack_pid, UINT32_MAX, buf)); WT_ERR(__wt_open(session, (const char *)buf->data, WT_FS_OPEN_FILE_TYPE_REGULAR, WT_FS_OPEN_CREATE, &conn->optrack_map_fh)); WT_ERR(__wt_spin_init(session, &conn->optrack_map_spinlock, "optrack map spinlock")); WT_ERR(__wt_malloc(session, WT_OPTRACK_BUFSIZE, &conn->dummy_session.optrack_buf)); /* Set operation tracking on */ F_SET(conn, WT_CONN_OPTRACK); err: __wt_scr_free(session, &buf); 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_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); }