/* * __backup_all -- * Backup all objects in the database. */ static int __backup_all(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb) { WT_CONFIG_ITEM cval; WT_CURSOR *cursor; WT_DECL_RET; const char *key, *value; cursor = NULL; /* Copy all of the metadata entries to the hot backup file. */ WT_RET(__wt_metadata_cursor(session, &cursor)); while ((ret = cursor->next(cursor)) == 0) { WT_ERR(cursor->get_key(cursor, &key)); WT_ERR(cursor->get_value(cursor, &value)); WT_ERR(__wt_fprintf(cb->bfp, "%s\n%s\n", key, value)); /* * While reading the metadata file, check there are no "sources" * or "types" which can't support hot backup. This checks for * a data source that's non-standard, which can't be backed up, * but is also sanity checking: if there's an entry backed by * anything other than a file or lsm entry, we're confused. */ if ((ret = __wt_config_getones( session, value, "type", &cval)) == 0 && !WT_PREFIX_MATCH_LEN(cval.str, cval.len, "file") && !WT_PREFIX_MATCH_LEN(cval.str, cval.len, "lsm")) WT_ERR_MSG(session, ENOTSUP, "hot backup is not supported for objects of " "type %.*s", (int)cval.len, cval.str); WT_ERR_NOTFOUND_OK(ret); if ((ret =__wt_config_getones( session, value, "source", &cval)) == 0 && !WT_PREFIX_MATCH_LEN(cval.str, cval.len, "file:") && !WT_PREFIX_MATCH_LEN(cval.str, cval.len, "lsm:")) WT_ERR_MSG(session, ENOTSUP, "hot backup is not supported for objects of " "source %.*s", (int)cval.len, cval.str); WT_ERR_NOTFOUND_OK(ret); } WT_ERR_NOTFOUND_OK(ret); WT_ERR(__wt_metadata_cursor_release(session, &cursor)); /* Build a list of the file objects that need to be copied. */ WT_WITH_HANDLE_LIST_LOCK(session, ret = __wt_meta_btree_apply(session, __backup_list_all_append, NULL)); err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __wt_metadata_update -- * Update a row in the metadata. */ int __wt_metadata_update( WT_SESSION_IMPL *session, const char *key, const char *value) { WT_CURSOR *cursor; WT_DECL_RET; WT_RET(__wt_verbose(session, WT_VERB_METADATA, "Update: key: %s, value: %s, tracking: %s, %s" "turtle", key, value, WT_META_TRACKING(session) ? "true" : "false", __metadata_turtle(key) ? "" : "not ")); if (__metadata_turtle(key)) { WT_WITH_TURTLE_LOCK(session, ret, ret = __wt_turtle_update(session, key, value)); return (ret); } if (WT_META_TRACKING(session)) WT_RET(__wt_meta_track_update(session, key)); WT_RET(__wt_metadata_cursor(session, &cursor)); /* This cursor needs to have overwrite semantics. */ WT_ASSERT(session, F_ISSET(cursor, WT_CURSTD_OVERWRITE)); cursor->set_key(cursor, key); cursor->set_value(cursor, value); WT_ERR(cursor->insert(cursor)); err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __wt_metadata_insert -- * Insert a row into the metadata. */ int __wt_metadata_insert( WT_SESSION_IMPL *session, const char *key, const char *value) { WT_CURSOR *cursor; WT_DECL_RET; WT_RET(__wt_verbose(session, WT_VERB_METADATA, "Insert: key: %s, value: %s, tracking: %s, %s" "turtle", key, value, WT_META_TRACKING(session) ? "true" : "false", __metadata_turtle(key) ? "" : "not ")); if (__metadata_turtle(key)) WT_RET_MSG(session, EINVAL, "%s: insert not supported on the turtle file", key); WT_RET(__wt_metadata_cursor(session, &cursor)); cursor->set_key(cursor, key); cursor->set_value(cursor, value); WT_ERR(cursor->insert(cursor)); if (WT_META_TRACKING(session)) WT_ERR(__wt_meta_track_insert(session, key)); err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __wt_meta_btree_apply -- * Apply a function to all files listed in the metadata, apart from the * metadata file. */ int __wt_meta_btree_apply(WT_SESSION_IMPL *session, int (*func)(WT_SESSION_IMPL *, const char *[]), const char *cfg[]) { WT_CURSOR *cursor; WT_DECL_RET; WT_RET(__wt_metadata_cursor(session, &cursor)); WT_SAVE_DHANDLE(session, ret = __meta_btree_apply(session, cursor, func, cfg)); WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __wt_metadata_search -- * Return a copied row from the metadata. * The caller is responsible for freeing the allocated memory. */ int __wt_metadata_search(WT_SESSION_IMPL *session, const char *key, char **valuep) { WT_CURSOR *cursor; WT_DECL_RET; const char *value; *valuep = NULL; __wt_verbose(session, WT_VERB_METADATA, "Search: key: %s, tracking: %s, %s" "turtle", key, WT_META_TRACKING(session) ? "true" : "false", __metadata_turtle(key) ? "" : "not "); if (__metadata_turtle(key)) { /* * The returned value should only be set if ret is non-zero, but * Coverity is convinced otherwise. The code path is used enough * that Coverity complains a lot, add an error check to get some * peace and quiet. */ if ((ret = __wt_turtle_read(session, key, valuep)) != 0) __wt_free(session, *valuep); return (ret); } /* * All metadata reads are at read-uncommitted isolation. That's * because once a schema-level operation completes, subsequent * operations must see the current version of checkpoint metadata, or * they may try to read blocks that may have been freed from a file. * Metadata updates use non-transactional techniques (such as the * schema and metadata locks) to protect access to in-flight updates. */ WT_RET(__wt_metadata_cursor(session, &cursor)); cursor->set_key(cursor, key); WT_WITH_TXN_ISOLATION(session, WT_ISO_READ_UNCOMMITTED, ret = cursor->search(cursor)); WT_ERR(ret); WT_ERR(cursor->get_value(cursor, &value)); WT_ERR(__wt_strdup(session, value, valuep)); err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); if (ret != 0) __wt_free(session, *valuep); return (ret); }
/* * __wt_meta_apply_all -- * Apply a function to all files listed in the metadata, apart from the * metadata file. */ int __wt_meta_apply_all(WT_SESSION_IMPL *session, int (*file_func)(WT_SESSION_IMPL *, const char *[]), int (*name_func)(WT_SESSION_IMPL *, const char *, bool *), const char *cfg[]) { WT_CURSOR *cursor; WT_DECL_RET; WT_ASSERT(session, F_ISSET(session, WT_SESSION_LOCKED_SCHEMA)); WT_RET(__wt_metadata_cursor(session, &cursor)); WT_SAVE_DHANDLE(session, ret = __meta_btree_apply(session, cursor, file_func, name_func, cfg)); WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __metadata_load_bulk -- * Create any bulk-loaded file stubs. */ static int __metadata_load_bulk(WT_SESSION_IMPL *session) { WT_CURSOR *cursor; WT_DECL_RET; uint32_t allocsize; bool exist; const char *filecfg[] = { WT_CONFIG_BASE(session, file_meta), NULL, NULL }; const char *key, *value; /* * If a file was being bulk-loaded during the hot backup, it will appear * in the metadata file, but the file won't exist. Create on demand. */ WT_RET(__wt_metadata_cursor(session, &cursor)); while ((ret = cursor->next(cursor)) == 0) { WT_ERR(cursor->get_key(cursor, &key)); if (!WT_PREFIX_SKIP(key, "file:")) continue; /* If the file exists, it's all good. */ WT_ERR(__wt_fs_exist(session, key, &exist)); if (exist) continue; /* * If the file doesn't exist, assume it's a bulk-loaded file; * retrieve the allocation size and re-create the file. */ WT_ERR(cursor->get_value(cursor, &value)); filecfg[1] = value; WT_ERR(__wt_direct_io_size_check( session, filecfg, "allocation_size", &allocsize)); WT_ERR(__wt_block_manager_create(session, key, allocsize)); } WT_ERR_NOTFOUND_OK(ret); err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __wt_metadata_remove -- * Remove a row from the metadata. */ int __wt_metadata_remove(WT_SESSION_IMPL *session, const char *key) { WT_CURSOR *cursor; WT_DECL_RET; __wt_verbose(session, WT_VERB_METADATA, "Remove: key: %s, tracking: %s, %s" "turtle", key, WT_META_TRACKING(session) ? "true" : "false", __metadata_turtle(key) ? "" : "not "); if (__metadata_turtle(key)) WT_RET_MSG(session, EINVAL, "%s: remove not supported on the turtle file", key); WT_RET(__wt_metadata_cursor(session, &cursor)); cursor->set_key(cursor, key); WT_ERR(cursor->search(cursor)); if (WT_META_TRACKING(session)) WT_ERR(__wt_meta_track_update(session, key)); WT_ERR(cursor->remove(cursor)); err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __wt_metadata_search -- * Return a copied row from the metadata. * The caller is responsible for freeing the allocated memory. */ int __wt_metadata_search(WT_SESSION_IMPL *session, const char *key, char **valuep) { WT_CURSOR *cursor; WT_DECL_RET; const char *value; *valuep = NULL; WT_RET(__wt_verbose(session, WT_VERB_METADATA, "Search: key: %s, tracking: %s, %s" "turtle", key, WT_META_TRACKING(session) ? "true" : "false", __metadata_turtle(key) ? "" : "not ")); if (__metadata_turtle(key)) return (__wt_turtle_read(session, key, valuep)); /* * All metadata reads are at read-uncommitted isolation. That's * because once a schema-level operation completes, subsequent * operations must see the current version of checkpoint metadata, or * they may try to read blocks that may have been freed from a file. * Metadata updates use non-transactional techniques (such as the * schema and metadata locks) to protect access to in-flight updates. */ WT_RET(__wt_metadata_cursor(session, &cursor)); cursor->set_key(cursor, key); WT_WITH_TXN_ISOLATION(session, WT_ISO_READ_UNCOMMITTED, ret = cursor->search(cursor)); WT_ERR(ret); WT_ERR(cursor->get_value(cursor, &value)); WT_ERR(__wt_strdup(session, value, valuep)); err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __schema_open_table -- * Open a named table (internal version). */ static int __schema_open_table(WT_SESSION_IMPL *session, const char *name, size_t namelen, bool ok_incomplete, WT_TABLE **tablep) { WT_CONFIG cparser; WT_CONFIG_ITEM ckey, cval; WT_CURSOR *cursor; WT_DECL_ITEM(buf); WT_DECL_RET; WT_TABLE *table; const char *tconfig; char *tablename; cursor = NULL; table = NULL; tablename = NULL; WT_ASSERT(session, F_ISSET(session, WT_SESSION_LOCKED_TABLE)); WT_ERR(__wt_scr_alloc(session, 0, &buf)); WT_ERR(__wt_buf_fmt(session, buf, "table:%.*s", (int)namelen, name)); WT_ERR(__wt_strndup(session, buf->data, buf->size, &tablename)); WT_ERR(__wt_metadata_cursor(session, &cursor)); cursor->set_key(cursor, tablename); WT_ERR(cursor->search(cursor)); WT_ERR(cursor->get_value(cursor, &tconfig)); WT_ERR(__wt_calloc_one(session, &table)); table->name = tablename; tablename = NULL; table->name_hash = __wt_hash_city64(name, namelen); WT_ERR(__wt_config_getones(session, tconfig, "columns", &cval)); WT_ERR(__wt_config_getones(session, tconfig, "key_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &table->key_format)); WT_ERR(__wt_config_getones(session, tconfig, "value_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &table->value_format)); WT_ERR(__wt_strdup(session, tconfig, &table->config)); /* Point to some items in the copy to save re-parsing. */ WT_ERR(__wt_config_getones(session, table->config, "columns", &table->colconf)); /* * Count the number of columns: tables are "simple" if the columns * are not named. */ WT_ERR(__wt_config_subinit(session, &cparser, &table->colconf)); table->is_simple = true; while ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0) table->is_simple = false; if (ret != WT_NOTFOUND) goto err; /* Check that the columns match the key and value formats. */ if (!table->is_simple) WT_ERR(__wt_schema_colcheck(session, table->key_format, table->value_format, &table->colconf, &table->nkey_columns, NULL)); WT_ERR(__wt_config_getones(session, table->config, "colgroups", &table->cgconf)); /* Count the number of column groups. */ WT_ERR(__wt_config_subinit(session, &cparser, &table->cgconf)); table->ncolgroups = 0; while ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0) ++table->ncolgroups; if (ret != WT_NOTFOUND) goto err; if (table->ncolgroups > 0 && table->is_simple) WT_ERR_MSG(session, EINVAL, "%s requires a table with named columns", tablename); WT_ERR(__wt_calloc_def(session, WT_COLGROUPS(table), &table->cgroups)); WT_ERR(__wt_schema_open_colgroups(session, table)); if (!ok_incomplete && !table->cg_complete) WT_ERR_MSG(session, EINVAL, "'%s' cannot be used " "until all column groups are created", table->name); /* Copy the schema generation into the new table. */ table->schema_gen = S2C(session)->schema_gen; *tablep = table; if (0) { err: WT_TRET(__wt_schema_destroy_table(session, &table)); } WT_TRET(__wt_metadata_cursor_release(session, &cursor)); __wt_free(session, tablename); __wt_scr_free(session, &buf); return (ret); }
/* * __schema_open_index -- * Open one or more indices for a table (internal version). */ static int __schema_open_index(WT_SESSION_IMPL *session, WT_TABLE *table, const char *idxname, size_t len, WT_INDEX **indexp) { WT_CURSOR *cursor; WT_DECL_ITEM(tmp); WT_DECL_RET; WT_INDEX *idx; u_int i; int cmp; bool match; const char *idxconf, *name, *tablename, *uri; /* Check if we've already done the work. */ if (idxname == NULL && table->idx_complete) return (0); cursor = NULL; idx = NULL; match = false; /* Build a search key. */ tablename = table->name; (void)WT_PREFIX_SKIP(tablename, "table:"); WT_ERR(__wt_scr_alloc(session, 512, &tmp)); WT_ERR(__wt_buf_fmt(session, tmp, "index:%s:", tablename)); /* Find matching indices. */ WT_ERR(__wt_metadata_cursor(session, &cursor)); cursor->set_key(cursor, tmp->data); if ((ret = cursor->search_near(cursor, &cmp)) == 0 && cmp < 0) ret = cursor->next(cursor); for (i = 0; ret == 0; i++, ret = cursor->next(cursor)) { WT_ERR(cursor->get_key(cursor, &uri)); name = uri; if (!WT_PREFIX_SKIP(name, tmp->data)) break; /* Is this the index we are looking for? */ match = idxname == NULL || WT_STRING_MATCH(name, idxname, len); /* * Ensure there is space, including if we have to make room for * a new entry in the middle of the list. */ WT_ERR(__wt_realloc_def(session, &table->idx_alloc, WT_MAX(i, table->nindices) + 1, &table->indices)); /* Keep the in-memory list in sync with the metadata. */ cmp = 0; while (table->indices[i] != NULL && (cmp = strcmp(uri, table->indices[i]->name)) > 0) { /* Index no longer exists, remove it. */ __wt_free(session, table->indices[i]); memmove(&table->indices[i], &table->indices[i + 1], (table->nindices - i) * sizeof(WT_INDEX *)); table->indices[--table->nindices] = NULL; } if (cmp < 0) { /* Make room for a new index. */ memmove(&table->indices[i + 1], &table->indices[i], (table->nindices - i) * sizeof(WT_INDEX *)); table->indices[i] = NULL; ++table->nindices; } if (!match) continue; if (table->indices[i] == NULL) { WT_ERR(cursor->get_value(cursor, &idxconf)); WT_ERR(__wt_calloc_one(session, &idx)); WT_ERR(__wt_strdup(session, uri, &idx->name)); WT_ERR(__wt_strdup(session, idxconf, &idx->config)); WT_ERR(__open_index(session, table, idx)); /* * If we're checking the creation of an index before a * table is fully created, don't save the index: it * will need to be reopened once the table is complete. */ if (!table->cg_complete) { WT_ERR( __wt_schema_destroy_index(session, &idx)); if (idxname != NULL) break; continue; } table->indices[i] = idx; idx = NULL; /* * If the slot is bigger than anything else we've seen, * bump the number of indices. */ if (i >= table->nindices) table->nindices = i + 1; } /* If we were looking for a single index, we're done. */ if (indexp != NULL) *indexp = table->indices[i]; if (idxname != NULL) break; } WT_ERR_NOTFOUND_OK(ret); if (idxname != NULL && !match) ret = WT_NOTFOUND; /* If we did a full pass, we won't need to do it again. */ if (idxname == NULL) { table->nindices = i; table->idx_complete = true; } err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); WT_TRET(__wt_schema_destroy_index(session, &idx)); __wt_scr_free(session, &tmp); return (ret); }