From 67d7eadd2e0db8c66c9105903401646501aa0138 Mon Sep 17 00:00:00 2001 From: Eric Milkie Date: Thu, 18 Dec 2014 13:46:06 -0500 Subject: SERVER-16367 Add versioning info to collections --- .../db/storage/wiredtiger/wiredtiger_index.cpp | 11 ++ .../storage/wiredtiger/wiredtiger_index_test.cpp | 27 +++- .../storage/wiredtiger/wiredtiger_record_store.cpp | 72 ++++----- .../wiredtiger/wiredtiger_record_store_test.cpp | 25 +++ .../db/storage/wiredtiger/wiredtiger_util.cpp | 136 ++++++++++++++++ src/mongo/db/storage/wiredtiger/wiredtiger_util.h | 28 ++++ .../db/storage/wiredtiger/wiredtiger_util_test.cpp | 177 +++++++++++++++++++++ 7 files changed, 437 insertions(+), 39 deletions(-) diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp index 88442a40711..2e389ea316a 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp @@ -400,6 +400,17 @@ namespace { } invariant(output); + + { + BSONObjBuilder metadata(output->subobjStart("metadata")); + Status status = WiredTigerUtil::getApplicationMetadata(txn, uri(), &metadata); + if (!status.isOK()) { + metadata.append("error", "unable to retrieve metadata"); + metadata.append("code", static_cast(status.code())); + metadata.append("reason", status.reason()); + } + } + WiredTigerSession* session = WiredTigerRecoveryUnit::get(txn)->getSession(); WT_SESSION* s = session->getSession(); Status status = WiredTigerUtil::exportTableToBSON(s, "statistics:" + uri(), diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_index_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_index_test.cpp index c2a128688f5..10e4a5524ac 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_index_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_index_test.cpp @@ -30,6 +30,9 @@ #include "mongo/platform/basic.h" +#include + +#include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/catalog/index_catalog_entry.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/json.h" @@ -141,4 +144,26 @@ namespace mongo { ASSERT_NOT_EQUALS(std::string::npos, config.find("abc=def")); } -} + TEST(WiredTigerIndexTest, FullValidateMetadata) { + MyHarnessHelper harnessHelper; + boost::scoped_ptr sorted(harnessHelper.newSortedDataInterface(false)); + boost::scoped_ptr opCtx(harnessHelper.newOperationContext()); + + long long numKeys = 0; + BSONObjBuilder bob; + sorted->fullValidate(opCtx.get(), true, &numKeys, &bob); + BSONObj obj = bob.obj(); + + BSONElement metadataElement = obj.getField("metadata"); + ASSERT_TRUE(metadataElement.isABSONObj()); + BSONObj metadata = metadataElement.Obj(); + + BSONElement versionElement = metadata.getField("formatVersion"); + ASSERT_TRUE(versionElement.isNumber()); + + BSONElement infoObjElement = metadata.getField("infoObj"); + ASSERT_EQUALS(mongo::String, infoObjElement.type()); + ASSERT_STRING_CONTAINS(infoObjElement.String(), "test.wt"); + } + +} // namespace mongo diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp index 37510238447..8530da8fbd3 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp @@ -56,43 +56,19 @@ namespace mongo { namespace { - bool shouldUseOplogHack(OperationContext* opCtx, const std::string& uri) { - WiredTigerCursor curwrap("metadata:", WiredTigerSession::kMetadataCursorId, opCtx); - WT_CURSOR* c = curwrap.get(); - c->set_key(c, uri.c_str()); - int ret = c->search(c); - if (ret == WT_NOTFOUND) - return false; - invariantWTOK(ret); - - const char* config = NULL; - c->get_value(c, &config); - invariant(config); + static const int kMinimumRecordStoreVersion = 1; + static const int kCurrentRecordStoreVersion = 1; // New record stores use this by default. + static const int kMaximumRecordStoreVersion = 1; + BOOST_STATIC_ASSERT(kCurrentRecordStoreVersion >= kMinimumRecordStoreVersion); + BOOST_STATIC_ASSERT(kCurrentRecordStoreVersion <= kMaximumRecordStoreVersion); - WiredTigerConfigParser topParser(config); - WT_CONFIG_ITEM metadata; - if (topParser.get("app_metadata", &metadata) != 0) - return false; - - if (metadata.len == 0) + bool shouldUseOplogHack(OperationContext* opCtx, const std::string& uri) { + StatusWith appMetadata = WiredTigerUtil::getApplicationMetadata(opCtx, uri); + if (!appMetadata.isOK()) { return false; - - WiredTigerConfigParser parser(metadata); - WT_CONFIG_ITEM keyItem; - WT_CONFIG_ITEM value; - while (parser.next(&keyItem, &value) == 0) { - const StringData key(keyItem.str, keyItem.len); - if (key == "oplogKeyExtractionVersion") { - if (value.type == WT_CONFIG_ITEM::WT_CONFIG_ITEM_NUM && value.val == 1) - return true; - } - - // This prevents downgrades with unsupported metadata settings. - severe() << "Unrecognized WiredTiger metadata setting: " << key << '=' << value.str; - fassertFailedNoTrace(28548); } - return false; + return (appMetadata.getValue().getIntField("oplogKeyExtractionVersion") == 1); } } // namespace @@ -155,16 +131,22 @@ namespace { if ( NamespaceString::oplog(ns) ) { // force file for oplog ss << "type=file,"; - ss << "app_metadata=(oplogKeyExtractionVersion=1),"; // Tune down to 10m. See SERVER-16247 ss << "memory_page_max=10m,"; } - else { - // Force this to be empty since users shouldn't be allowed to change it. - ss << "app_metadata=(),"; - } + + // WARNING: No user-specified config can appear below this line. These options are required + // for correct behavior of the server. ss << "key_format=q,value_format=u"; + + // Record store metadata + ss << ",app_metadata=(formatVersion=" << kCurrentRecordStoreVersion; + if (NamespaceString::oplog(ns)) { + ss << ",oplogKeyExtractionVersion=1"; + } + ss << ")"; + return StatusWith(ss); } @@ -189,6 +171,11 @@ namespace { _sizeStorer( sizeStorer ), _sizeStorerCounter(0) { + Status versionStatus = WiredTigerUtil::checkApplicationMetadataFormatVersion( + ctx, uri, kMinimumRecordStoreVersion, kMaximumRecordStoreVersion); + if (!versionStatus.isOK()) { + fassertFailedWithStatusNoTrace(28548, versionStatus); + } if (_isCapped) { invariant(_cappedMaxSize > 0); @@ -714,6 +701,15 @@ namespace { WiredTigerSession* session = WiredTigerRecoveryUnit::get(txn)->getSession(); WT_SESSION* s = session->getSession(); BSONObjBuilder bob(result->subobjStart(kWiredTigerEngineName)); + { + BSONObjBuilder metadata(bob.subobjStart("metadata")); + Status status = WiredTigerUtil::getApplicationMetadata(txn, GetURI(), &metadata); + if (!status.isOK()) { + metadata.append("error", "unable to retrieve metadata"); + metadata.append("code", static_cast(status.code())); + metadata.append("reason", status.reason()); + } + } Status status = WiredTigerUtil::exportTableToBSON(s, "statistics:" + GetURI(), "statistics=(fast)", &bob); if (!status.isOK()) { diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp index e96a3db36d3..8cba02bf02f 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp @@ -30,10 +30,12 @@ #include "mongo/platform/basic.h" +#include #include #include #include "mongo/base/string_data.h" +#include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/json.h" #include "mongo/db/operation_context_noop.h" @@ -48,6 +50,7 @@ namespace mongo { + using boost::scoped_ptr; using std::string; using std::stringstream; @@ -631,4 +634,26 @@ namespace mongo { ASSERT_THROWS(rs->storageSize(opCtx.get()), UserException); } + TEST(WiredTigerRecordStoreTest, AppendCustomStatsMetadata) { + WiredTigerHarnessHelper harnessHelper; + scoped_ptr rs(harnessHelper.newNonCappedRecordStore("a.b")); + + scoped_ptr opCtx(harnessHelper.newOperationContext()); + BSONObjBuilder builder; + rs->appendCustomStats(opCtx.get(), &builder, 1.0); + BSONObj customStats = builder.obj(); + + BSONElement wiredTigerElement = customStats.getField(kWiredTigerEngineName); + ASSERT_TRUE(wiredTigerElement.isABSONObj()); + BSONObj wiredTiger = wiredTigerElement.Obj(); + + BSONElement metadataElement = wiredTiger.getField("metadata"); + ASSERT_TRUE(metadataElement.isABSONObj()); + BSONObj metadata = metadataElement.Obj(); + + BSONElement versionElement = metadata.getField("formatVersion"); + ASSERT_TRUE(versionElement.isNumber()); + + } + } // namespace mongo diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp index df65c9d4601..9131643358c 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp @@ -39,6 +39,9 @@ #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/mongoutils/str.h" #include "mongo/util/scopeguard.h" @@ -70,6 +73,139 @@ namespace mongo { return Status(ErrorCodes::UnknownError, s); } + StatusWith WiredTigerUtil::getMetadata(OperationContext* opCtx, + const StringData& uri) { + invariant(opCtx); + WiredTigerCursor curwrap("metadata:", WiredTigerSession::kMetadataCursorId, opCtx); + WT_CURSOR* cursor = curwrap.get(); + cursor->set_key(cursor, uri); + 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) { + 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); + } + + return Status::OK(); + } + // static StatusWith WiredTigerUtil::getStatisticsValue(WT_SESSION* session, const std::string& uri, diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_util.h b/src/mongo/db/storage/wiredtiger/wiredtiger_util.h index b40c271a860..aeaeaa72a13 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_util.h +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_util.h @@ -36,11 +36,14 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" #include "mongo/base/status_with.h" +#include "mongo/bson/bsonobj.h" #include "mongo/util/assert_util.h" namespace mongo { class BSONObjBuilder; + class OperationContext; + class WiredTigerConfigParser; inline bool wt_keeptxnopen() { return false; @@ -96,6 +99,31 @@ namespace mongo { const std::string& uri, const std::string& config, BSONObjBuilder* bob); + /** + * Gets entire metadata string for collection/index at URI. + */ + static StatusWith getMetadata(OperationContext* opCtx, + const StringData& uri); + + /** + * Reads app_metadata for collection/index at URI as a BSON document. + */ + static Status getApplicationMetadata(OperationContext* opCtx, + const StringData& uri, + BSONObjBuilder* bob); + + static StatusWith getApplicationMetadata(OperationContext* opCtx, + const StringData& uri); + + /** + * Validates formatVersion in application metadata for 'uri'. + * Version must be numeric and be in the range [minimumVersion, maximumVersion]. + * URI is used in error messages only. + */ + static Status checkApplicationMetadataFormatVersion(OperationContext* opCtx, + const StringData& uri, + int64_t minimumVersion, + int64_t maximumVersion); /** * Reads individual statistics using URI. * List of statistics keys WT_STAT_* can be found in wiredtiger.h. diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_util_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_util_test.cpp index 328c8041a2b..cffc1a01284 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_util_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_util_test.cpp @@ -30,10 +30,12 @@ #include "mongo/platform/basic.h" +#include #include #include #include "mongo/base/string_data.h" +#include "mongo/db/operation_context_noop.h" #include "mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h" #include "mongo/db/storage/wiredtiger/wiredtiger_session_cache.h" #include "mongo/db/storage/wiredtiger/wiredtiger_util.h" @@ -76,12 +78,187 @@ namespace mongo { return &_sessionCache; } + OperationContext* newOperationContext() { + return new OperationContextNoop(new WiredTigerRecoveryUnit(getSessionCache())); + } + private: unittest::TempDir _dbpath; WiredTigerConnection _connection; WiredTigerSessionCache _sessionCache; }; + class WiredTigerUtilMetadataTest : public mongo::unittest::Test { + public: + virtual void setUp() { + _harnessHelper.reset(new WiredTigerUtilHarnessHelper("")); + _opCtx.reset(_harnessHelper->newOperationContext()); + } + + virtual void tearDown() { + _opCtx.reset(NULL); + _harnessHelper.reset(NULL); + } + + protected: + const char* getURI() const { + return "table:mytable"; + } + + OperationContext* getOperationContext() const { + ASSERT(_opCtx.get()); + return _opCtx.get(); + } + + void createSession(const char* config) { + WT_SESSION* wtSession = + WiredTigerRecoveryUnit::get(_opCtx.get())->getSession()->getSession(); + ASSERT_OK(wtRCToStatus(wtSession->create(wtSession, getURI(), config))); + } + private: + boost::scoped_ptr _harnessHelper; + boost::scoped_ptr _opCtx; + }; + + TEST_F(WiredTigerUtilMetadataTest, GetConfigurationStringInvalidURI) { + StatusWith result = + WiredTigerUtil::getMetadata(getOperationContext(), getURI()); + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(ErrorCodes::NoSuchKey, result.getStatus().code()); + } + + TEST_F(WiredTigerUtilMetadataTest, GetConfigurationStringNull) { + const char* config = NULL; + createSession(config); + StatusWith result = + WiredTigerUtil::getMetadata(getOperationContext(), getURI()); + ASSERT_OK(result.getStatus()); + ASSERT_FALSE(result.getValue().empty()); + } + + TEST_F(WiredTigerUtilMetadataTest, GetConfigurationStringSimple) { + const char* config = "app_metadata=(abc=123)"; + createSession(config); + StatusWith result = + WiredTigerUtil::getMetadata(getOperationContext(), getURI()); + ASSERT_OK(result.getStatus()); + ASSERT_STRING_CONTAINS(result.getValue(), config); + } + + TEST_F(WiredTigerUtilMetadataTest, GetApplicationMetadataInvalidURI) { + StatusWith result = + WiredTigerUtil::getApplicationMetadata(getOperationContext(), getURI()); + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(ErrorCodes::NoSuchKey, result.getStatus().code()); + } + + TEST_F(WiredTigerUtilMetadataTest, GetApplicationMetadataNull) { + const char* config = NULL; + createSession(config); + StatusWith result = + WiredTigerUtil::getApplicationMetadata(getOperationContext(), getURI()); + ASSERT_OK(result.getStatus()); + ASSERT_TRUE(result.getValue().isEmpty()); + } + + TEST_F(WiredTigerUtilMetadataTest, GetApplicationMetadataString) { + const char* config = "app_metadata=\"abc\""; + createSession(config); + StatusWith result = + WiredTigerUtil::getApplicationMetadata(getOperationContext(), getURI()); + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(ErrorCodes::FailedToParse, result.getStatus().code()); + } + + TEST_F(WiredTigerUtilMetadataTest, GetApplicationMetadataInvalidMetadata) { + const char* config = "app_metadata=(abc=def=ghi)"; + createSession(config); + StatusWith result = + WiredTigerUtil::getApplicationMetadata(getOperationContext(), getURI()); + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(ErrorCodes::BadValue, result.getStatus().code()); + } + + TEST_F(WiredTigerUtilMetadataTest, GetApplicationMetadataDuplicateKeys) { + const char* config = "app_metadata=(abc=123,abc=456)"; + createSession(config); + StatusWith result = + WiredTigerUtil::getApplicationMetadata(getOperationContext(), getURI()); + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(ErrorCodes::DuplicateKey, result.getStatus().code()); + } + + TEST_F(WiredTigerUtilMetadataTest, GetApplicationMetadataTypes) { + const char* config = "app_metadata=(stringkey=\"abc\",boolkey1=true,boolkey2=false," + "idkey=def,numkey=123," + "structkey=(k1=v2,k2=v2))"; + createSession(config); + StatusWith result = + WiredTigerUtil::getApplicationMetadata(getOperationContext(), getURI()); + ASSERT_OK(result.getStatus()); + const BSONObj& obj = result.getValue(); + + BSONElement stringElement = obj.getField("stringkey"); + ASSERT_EQUALS(mongo::String, stringElement.type()); + ASSERT_EQUALS("abc", stringElement.String()); + + BSONElement boolElement1 = obj.getField("boolkey1"); + ASSERT_TRUE(boolElement1.isBoolean()); + ASSERT_TRUE(boolElement1.boolean()); + + BSONElement boolElement2 = obj.getField("boolkey2"); + ASSERT_TRUE(boolElement2.isBoolean()); + ASSERT_FALSE(boolElement2.boolean()); + + BSONElement identifierElement = obj.getField("idkey"); + ASSERT_EQUALS(mongo::String, identifierElement.type()); + ASSERT_EQUALS("def", identifierElement.String()); + + BSONElement numberElement = obj.getField("numkey"); + ASSERT_TRUE(numberElement.isNumber()); + ASSERT_EQUALS(123, numberElement.numberInt()); + + BSONElement structElement = obj.getField("structkey"); + ASSERT_EQUALS(mongo::String, structElement.type()); + ASSERT_EQUALS("(k1=v2,k2=v2)", structElement.String()); + } + + TEST_F(WiredTigerUtilMetadataTest, CheckApplicationMetadataFormatVersionMissingKey) { + createSession("app_metadata=(abc=123)"); + ASSERT_OK(WiredTigerUtil::checkApplicationMetadataFormatVersion(getOperationContext(), + getURI(), + 1, + 1)); + ASSERT_NOT_OK(WiredTigerUtil::checkApplicationMetadataFormatVersion(getOperationContext(), + getURI(), + 2, + 2)); + } + + TEST_F(WiredTigerUtilMetadataTest, CheckApplicationMetadataFormatVersionString) { + createSession("app_metadata=(formatVersion=\"bar\")"); + ASSERT_NOT_OK(WiredTigerUtil::checkApplicationMetadataFormatVersion(getOperationContext(), + getURI(), + 1, + 1)); + } + + TEST_F(WiredTigerUtilMetadataTest, CheckApplicationMetadataFormatVersionNumber) { + createSession("app_metadata=(formatVersion=2)"); + ASSERT_OK(WiredTigerUtil::checkApplicationMetadataFormatVersion(getOperationContext(), + getURI(), + 2, + 3)); + ASSERT_NOT_OK(WiredTigerUtil::checkApplicationMetadataFormatVersion(getOperationContext(), + getURI(), + 1, + 1)); + ASSERT_NOT_OK(WiredTigerUtil::checkApplicationMetadataFormatVersion(getOperationContext(), + getURI(), + 3, + 3)); + } + TEST(WiredTigerUtilTest, GetStatisticsValueMissingTable) { WiredTigerUtilHarnessHelper harnessHelper("statistics=(all)"); WiredTigerRecoveryUnit recoveryUnit(harnessHelper.getSessionCache()); -- cgit v1.2.1