/*- * Copyright (c) 2014-2019 MongoDB, Inc. * Copyright (c) 2008-2014 WiredTiger, Inc. * All rights reserved. * * See the file LICENSE for redistribution information. */ #include "wt_internal.h" static int __schema_alter(WT_SESSION_IMPL *, const char *, const char *[]); /* * __alter_apply -- * Alter an object */ static int __alter_apply(WT_SESSION_IMPL *session, const char *uri, const char *newcfg[], const char *base_config) { WT_DECL_RET; const char *cfg[4]; char *config, *newconfig; newconfig = NULL; /* Find the URI */ WT_RET(__wt_metadata_search(session, uri, &config)); WT_ASSERT(session, newcfg[0] != NULL); /* * Start with the base configuration because collapse is like * a projection and if we are reading older metadata, it may not * have all the components. */ cfg[0] = base_config; cfg[1] = config; cfg[2] = newcfg[0]; cfg[3] = NULL; WT_ERR(__wt_config_collapse(session, cfg, &newconfig)); /* * Only rewrite if there are changes. */ if (strcmp(config, newconfig) != 0) WT_ERR(__wt_metadata_update(session, uri, newconfig)); else WT_STAT_CONN_INCR(session, session_table_alter_skip); err: __wt_free(session, config); __wt_free(session, newconfig); /* * Map WT_NOTFOUND to ENOENT, based on the assumption WT_NOTFOUND means * there was no metadata entry. */ if (ret == WT_NOTFOUND) ret = __wt_set_return(session, ENOENT); return (ret); } /* * __alter_file -- * Alter a file. */ static int __alter_file(WT_SESSION_IMPL *session, const char *newcfg[]) { const char *uri; /* * We know that we have exclusive access to the file. So it will be * closed after we're done with it and the next open will see the * updated metadata. */ uri = session->dhandle->name; if (!WT_PREFIX_MATCH(uri, "file:")) return (__wt_unexpected_object_type(session, uri, "file:")); return (__alter_apply(session, uri, newcfg, WT_CONFIG_BASE(session, file_meta))); } /* * __alter_tree -- * Alter an index or colgroup reference. */ static int __alter_tree(WT_SESSION_IMPL *session, const char *name, const char *newcfg[]) { WT_CONFIG_ITEM cval; WT_DECL_ITEM(data_source); WT_DECL_RET; char *value; bool is_colgroup; value = NULL; is_colgroup = WT_PREFIX_MATCH(name, "colgroup:"); if (!is_colgroup && !WT_PREFIX_MATCH(name, "index:")) return (__wt_unexpected_object_type( session, name, "'colgroup:' or 'index:'")); /* Read the schema value. */ WT_ERR(__wt_metadata_search(session, name, &value)); /* Get the data source URI. */ if ((ret = __wt_config_getones(session, value, "source", &cval)) != 0) WT_ERR_MSG(session, EINVAL, "index or column group has no data source: %s", value); WT_ERR(__wt_scr_alloc(session, 0, &data_source)); WT_ERR(__wt_buf_fmt(session, data_source, "%.*s", (int)cval.len, cval.str)); /* Alter the data source */ WT_ERR(__schema_alter(session, data_source->data, newcfg)); /* Alter the index or colgroup */ if (is_colgroup) WT_ERR(__alter_apply(session, name, newcfg, WT_CONFIG_BASE(session, colgroup_meta))); else WT_ERR(__alter_apply(session, name, newcfg, WT_CONFIG_BASE(session, index_meta))); err: __wt_scr_free(session, &data_source); __wt_free(session, value); return (ret); } /* * __alter_table -- * Alter a table. */ static int __alter_table(WT_SESSION_IMPL *session, const char *uri, const char *newcfg[], bool exclusive_refreshed) { WT_COLGROUP *colgroup; WT_DECL_RET; WT_INDEX *idx; WT_TABLE *table; u_int i; const char *name; colgroup = NULL; table = NULL; name = uri; WT_PREFIX_SKIP_REQUIRED(session, name, "table:"); /* * If we have exclusive access update all objects in the schema for * this table and reopen the handle to update the in-memory state. */ if (exclusive_refreshed) { /* * Open the table so we can alter its column groups and indexes, * keeping the table locked exclusive across the alter. */ WT_RET(__wt_schema_get_table_uri(session, uri, true, WT_DHANDLE_EXCLUSIVE, &table)); /* * Meta tracking needs to be used because alter needs to be * atomic. */ WT_ASSERT(session, WT_META_TRACKING(session)); WT_WITH_DHANDLE(session, &table->iface, ret = __wt_meta_track_handle_lock(session, false)); WT_RET(ret); /* Alter the column groups. */ for (i = 0; i < WT_COLGROUPS(table); i++) { if ((colgroup = table->cgroups[i]) == NULL) continue; WT_RET(__alter_tree(session, colgroup->name, newcfg)); } /* Alter the indices. */ WT_RET(__wt_schema_open_indices(session, table)); for (i = 0; i < table->nindices; i++) { if ((idx = table->indices[i]) == NULL) continue; WT_RET(__alter_tree(session, idx->name, newcfg)); } } /* Alter the table */ WT_RET(__alter_apply(session, uri, newcfg, WT_CONFIG_BASE(session, table_meta))); return (ret); } /* * __schema_alter -- * Alter an object. */ static int __schema_alter(WT_SESSION_IMPL *session, const char *uri, const char *newcfg[]) { WT_CONFIG_ITEM cv; uint32_t flags; bool exclusive_refreshed; const char *cfg[] = { WT_CONFIG_BASE(session, WT_SESSION_alter), newcfg[0], NULL}; /* * Determine what configuration says about exclusive access. * A non exclusive alter that doesn't refresh in-memory configuration is * only valid for the table objects. */ WT_RET(__wt_config_gets(session, cfg, "exclusive_refreshed", &cv)); exclusive_refreshed = (bool)cv.val; if (!exclusive_refreshed && !WT_PREFIX_MATCH(uri, "table:")) WT_RET_MSG(session, EINVAL, "option \"exclusive_refreshed\" " "is applicable only on simple tables"); /* * The alter flag is used so LSM can apply some special logic, the * exclusive flag avoids conflicts with other operations and the lock * only flag is required because we don't need to have a handle to * update the metadata and opening the handle causes problems when * meta tracking is enabled. */ flags = WT_BTREE_ALTER | WT_DHANDLE_EXCLUSIVE | WT_DHANDLE_LOCK_ONLY; if (WT_PREFIX_MATCH(uri, "file:")) return (__wt_exclusive_handle_operation( session, uri, __alter_file, newcfg, flags)); if (WT_PREFIX_MATCH(uri, "colgroup:") || WT_PREFIX_MATCH(uri, "index:")) return (__alter_tree(session, uri, newcfg)); if (WT_PREFIX_MATCH(uri, "lsm:")) return (__wt_lsm_tree_worker(session, uri, __alter_file, NULL, newcfg, flags)); if (WT_PREFIX_MATCH(uri, "table:")) return (__alter_table(session, uri, newcfg, exclusive_refreshed)); return (__wt_bad_object_type(session, uri)); } /* * __wt_schema_alter -- * Alter an object. */ int __wt_schema_alter(WT_SESSION_IMPL *session, const char *uri, const char *newcfg[]) { WT_DECL_RET; WT_SESSION_IMPL *int_session; WT_RET(__wt_schema_internal_session(session, &int_session)); WT_ERR(__wt_meta_track_on(int_session)); ret = __schema_alter(int_session, uri, newcfg); WT_TRET(__wt_meta_track_off(int_session, true, ret != 0)); err: WT_TRET(__wt_schema_session_release(session, int_session)); return (ret); }