// wiredtiger_util.cpp /** * Copyright (C) 2014 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage #include "mongo/platform/basic.h" #include "mongo/db/storage/wiredtiger/wiredtiger_util.h" #include #include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h" #include "mongo/db/storage/wiredtiger/wiredtiger_session_cache.h" #include "mongo/platform/unordered_set.h" #include "mongo/util/assert_util.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" #include "mongo/util/scopeguard.h" namespace mongo { using std::string; Status wtRCToStatus_slow(int retCode, const char* prefix ) { if (retCode == 0) return Status::OK(); if ( retCode == WT_ROLLBACK ) { throw WriteConflictException(); } fassert( 28559, retCode != WT_PANIC ); str::stream s; if ( prefix ) s << prefix << " "; s << retCode << ": " << wiredtiger_strerror(retCode); if (retCode == EINVAL) { return Status(ErrorCodes::BadValue, s); } // TODO convert specific codes rather than just using UNKNOWN_ERROR for everything. return Status(ErrorCodes::UnknownError, s); } void WiredTigerUtil::fetchTypeAndSourceURI(OperationContext* opCtx, const std::string& tableUri, std::string* type, std::string* source) { std::string colgroupUri = "colgroup"; const size_t colon = tableUri.find(':'); invariant(colon != string::npos); colgroupUri += tableUri.substr(colon); StatusWith colgroupResult = getMetadata(opCtx, colgroupUri); invariant(colgroupResult.isOK()); WiredTigerConfigParser parser(colgroupResult.getValue()); WT_CONFIG_ITEM typeItem; invariant(parser.get("type", &typeItem) == 0); invariant(typeItem.type == WT_CONFIG_ITEM::WT_CONFIG_ITEM_ID); *type = std::string(typeItem.str, typeItem.len); WT_CONFIG_ITEM sourceItem; invariant(parser.get("source", &sourceItem) == 0); invariant(sourceItem.type == WT_CONFIG_ITEM::WT_CONFIG_ITEM_STRING); *source = std::string(sourceItem.str, sourceItem.len); } StatusWith WiredTigerUtil::getMetadata(OperationContext* opCtx, const StringData& uri) { invariant(opCtx); WiredTigerCursor curwrap("metadata:", WiredTigerSession::kMetadataCursorId, false, opCtx); WT_CURSOR* cursor = curwrap.get(); invariant(cursor); std::string strUri = uri.toString(); cursor->set_key(cursor, strUri.c_str()); int ret = cursor->search(cursor); if (ret == WT_NOTFOUND) { return StatusWith(ErrorCodes::NoSuchKey, str::stream() << "Unable to find metadata for " << uri); } else if (ret != 0) { return StatusWith(wtRCToStatus(ret)); } const char* metadata = NULL; ret = cursor->get_value(cursor, &metadata); if (ret != 0) { return StatusWith(wtRCToStatus(ret)); } invariant(metadata); return StatusWith(metadata); } Status WiredTigerUtil::getApplicationMetadata(OperationContext* opCtx, const StringData& uri, BSONObjBuilder* bob) { StatusWith metadataResult = getMetadata(opCtx, uri); if (!metadataResult.isOK()) { return metadataResult.getStatus(); } WiredTigerConfigParser topParser(metadataResult.getValue()); WT_CONFIG_ITEM appMetadata; if (topParser.get("app_metadata", &appMetadata) != 0) { return Status::OK(); } if (appMetadata.len == 0) { return Status::OK(); } if (appMetadata.type != WT_CONFIG_ITEM::WT_CONFIG_ITEM_STRUCT) { return Status(ErrorCodes::FailedToParse, str::stream() << "app_metadata must be a nested struct. Actual value: " << StringData(appMetadata.str, appMetadata.len)); } WiredTigerConfigParser parser(appMetadata); WT_CONFIG_ITEM keyItem; WT_CONFIG_ITEM valueItem; int ret; unordered_set keysSeen; while ((ret = parser.next(&keyItem, &valueItem)) == 0) { const StringData key(keyItem.str, keyItem.len); if (keysSeen.count(key)) { return Status(ErrorCodes::DuplicateKey, str::stream() << "app_metadata must not contain duplicate keys. " << "Found multiple instances of key '" << key << "'."); } keysSeen.insert(key); switch (valueItem.type) { case WT_CONFIG_ITEM::WT_CONFIG_ITEM_BOOL: bob->appendBool(key, valueItem.val); break; case WT_CONFIG_ITEM::WT_CONFIG_ITEM_NUM: bob->appendIntOrLL(key, valueItem.val); break; default: bob->append(key, StringData(valueItem.str, valueItem.len)); break; } } if (ret != WT_NOTFOUND) { return wtRCToStatus(ret); } return Status::OK(); } StatusWith WiredTigerUtil::getApplicationMetadata(OperationContext* opCtx, const StringData& uri) { BSONObjBuilder bob; Status status = getApplicationMetadata(opCtx, uri, &bob); if (!status.isOK()) { return StatusWith(status); } return StatusWith(bob.obj()); } Status WiredTigerUtil::checkApplicationMetadataFormatVersion(OperationContext* opCtx, const StringData& uri, int64_t minimumVersion, int64_t maximumVersion) { StatusWith result = getMetadata(opCtx, uri); if (result.getStatus().code() == ErrorCodes::NoSuchKey) { return result.getStatus(); } invariantOK(result.getStatus()); WiredTigerConfigParser topParser(result.getValue()); WT_CONFIG_ITEM metadata; if (topParser.get("app_metadata", &metadata) != 0) return Status(ErrorCodes::UnsupportedFormat, str::stream() << "application metadata for " << uri << " is missing "); WiredTigerConfigParser parser(metadata); int64_t version = 0; WT_CONFIG_ITEM versionItem; if (parser.get("formatVersion", &versionItem) != 0) { // If 'formatVersion' is missing, this metadata was introduced by // one of the RC versions (where the format version is 1). version = 1; } else if (versionItem.type == WT_CONFIG_ITEM::WT_CONFIG_ITEM_NUM) { version = versionItem.val; } else { return Status(ErrorCodes::UnsupportedFormat, str::stream() << "'formatVersion' in application metadata for " << uri << " must be a number. Current value: " << StringData(versionItem.str, versionItem.len)); } if (version < minimumVersion || version > maximumVersion) { return Status(ErrorCodes::UnsupportedFormat, str::stream() << "Application metadata for " << uri << " has unsupported format version " << version); } LOG(2) << "WiredTigerUtil::checkApplicationMetadataFormatVersion " << " uri: " << uri << " ok range " << minimumVersion << " -> " << maximumVersion << " current: " << version; return Status::OK(); } // static StatusWith WiredTigerUtil::getStatisticsValue(WT_SESSION* session, const std::string& uri, const std::string& config, int statisticsKey) { invariant(session); WT_CURSOR* cursor = NULL; const char* cursorConfig = config.empty() ? NULL : config.c_str(); int ret = session->open_cursor(session, uri.c_str(), NULL, cursorConfig, &cursor); if (ret != 0) { return StatusWith(ErrorCodes::CursorNotFound, str::stream() << "unable to open cursor at URI " << uri << ". reason: " << wiredtiger_strerror(ret)); } invariant(cursor); ON_BLOCK_EXIT(cursor->close, cursor); cursor->set_key(cursor, statisticsKey); ret = cursor->search(cursor); if (ret != 0) { return StatusWith(ErrorCodes::NoSuchKey, str::stream() << "unable to find key " << statisticsKey << " at URI " << uri << ". reason: " << wiredtiger_strerror(ret)); } uint64_t value; ret = cursor->get_value(cursor, NULL, NULL, &value); if (ret != 0) { return StatusWith(ErrorCodes::BadValue, str::stream() << "unable to get value for key " << statisticsKey << " at URI " << uri << ". reason: " << wiredtiger_strerror(ret)); } return StatusWith(value); } int64_t WiredTigerUtil::getIdentSize(WT_SESSION* s, const std::string& uri ) { StatusWith result = WiredTigerUtil::getStatisticsValueAs( s, "statistics:" + uri, "statistics=(fast)", WT_STAT_DSRC_BLOCK_SIZE); const Status& status = result.getStatus(); if ( !status.isOK() ) { if ( status.code() == ErrorCodes::CursorNotFound ) { // ident gone, so its 0 return 0; } uassertStatusOK( status ); } return result.getValue(); } Status WiredTigerUtil::exportTableToBSON(WT_SESSION* session, const std::string& uri, const std::string& config, BSONObjBuilder* bob) { invariant(session); invariant(bob); WT_CURSOR* c = NULL; const char* cursorConfig = config.empty() ? NULL : config.c_str(); int ret = session->open_cursor(session, uri.c_str(), NULL, cursorConfig, &c); if (ret != 0) { return Status(ErrorCodes::CursorNotFound, str::stream() << "unable to open cursor at URI " << uri << ". reason: " << wiredtiger_strerror(ret)); } bob->append("uri", uri); invariant(c); ON_BLOCK_EXIT(c->close, c); std::map subs; const char* desc; uint64_t value; while (c->next(c) == 0 && c->get_value(c, &desc, NULL, &value) == 0) { StringData key( desc ); StringData prefix; StringData suffix; size_t idx = key.find( ':' ); if ( idx != string::npos ) { prefix = key.substr( 0, idx ); suffix = key.substr( idx + 1 ); } else { idx = key.find( ' ' ); } if ( idx != string::npos ) { prefix = key.substr( 0, idx ); suffix = key.substr( idx + 1 ); } else { prefix = key; suffix = "num"; } long long v = _castStatisticsValue(value); if ( prefix.size() == 0 ) { bob->appendNumber(desc, v); } else { BSONObjBuilder*& sub = subs[prefix.toString()]; if ( !sub ) sub = new BSONObjBuilder(); sub->appendNumber(mongoutils::str::ltrim(suffix.toString()), v); } } for ( std::map::const_iterator it = subs.begin(); it != subs.end(); ++it ) { const std::string& s = it->first; bob->append( s, it->second->obj() ); delete it->second; } return Status::OK(); } } // namespace mongo