1 // kv_catalog.cpp
2
3
4 /**
5 * Copyright (C) 2018-present MongoDB, Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the Server Side Public License, version 1,
9 * as published by MongoDB, Inc.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * Server Side Public License for more details.
15 *
16 * You should have received a copy of the Server Side Public License
17 * along with this program. If not, see
18 * <http://www.mongodb.com/licensing/server-side-public-license>.
19 *
20 * As a special exception, the copyright holders give permission to link the
21 * code of portions of this program with the OpenSSL library under certain
22 * conditions as described in each individual source file and distribute
23 * linked combinations including the program with the OpenSSL library. You
24 * must comply with the Server Side Public License in all respects for
25 * all of the code used other than as permitted herein. If you modify file(s)
26 * with this exception, you may extend this exception to your version of the
27 * file(s), but you are not obligated to do so. If you do not wish to do so,
28 * delete this exception statement from your version. If you delete this
29 * exception statement from all source files in the program, then also delete
30 * it in the license file.
31 */
32
33 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
34
35 #include "mongo/db/storage/kv/kv_catalog.h"
36
37 #include <stdlib.h>
38
39 #include "mongo/bson/util/bson_extract.h"
40 #include "mongo/bson/util/builder.h"
41 #include "mongo/db/concurrency/d_concurrency.h"
42 #include "mongo/db/namespace_string.h"
43 #include "mongo/db/operation_context.h"
44 #include "mongo/db/storage/kv/kv_catalog_feature_tracker.h"
45 #include "mongo/db/storage/record_store.h"
46 #include "mongo/db/storage/recovery_unit.h"
47 #include "mongo/platform/bits.h"
48 #include "mongo/platform/random.h"
49 #include "mongo/stdx/memory.h"
50 #include "mongo/util/log.h"
51 #include "mongo/util/mongoutils/str.h"
52
53 namespace mongo {
54 namespace {
55 // This is a global resource, which protects accesses to the catalog metadata (instance-wide).
56 // It is never used with KVEngines that support doc-level locking so this should never conflict
57 // with anything else.
58
59 const char kIsFeatureDocumentFieldName[] = "isFeatureDoc";
60 const char kNamespaceFieldName[] = "ns";
61 const char kNonRepairableFeaturesFieldName[] = "nonRepairable";
62 const char kRepairableFeaturesFieldName[] = "repairable";
63
appendPositionsOfBitsSet(uint64_t value,StringBuilder * sb)64 void appendPositionsOfBitsSet(uint64_t value, StringBuilder* sb) {
65 invariant(sb);
66
67 *sb << "[ ";
68 bool firstIteration = true;
69 while (value) {
70 const int lowestSetBitPosition = countTrailingZeros64(value);
71 if (!firstIteration) {
72 *sb << ", ";
73 }
74 *sb << lowestSetBitPosition;
75 value ^= (1ULL << lowestSetBitPosition);
76 firstIteration = false;
77 }
78 *sb << " ]";
79 }
80 }
81
82 using std::unique_ptr;
83 using std::string;
84
85 class KVCatalog::AddIdentChange : public RecoveryUnit::Change {
86 public:
AddIdentChange(KVCatalog * catalog,StringData ident)87 AddIdentChange(KVCatalog* catalog, StringData ident)
88 : _catalog(catalog), _ident(ident.toString()) {}
89
commit()90 virtual void commit() {}
rollback()91 virtual void rollback() {
92 stdx::lock_guard<stdx::mutex> lk(_catalog->_identsLock);
93 _catalog->_idents.erase(_ident);
94 }
95
96 KVCatalog* const _catalog;
97 const std::string _ident;
98 };
99
100 class KVCatalog::RemoveIdentChange : public RecoveryUnit::Change {
101 public:
RemoveIdentChange(KVCatalog * catalog,StringData ident,const Entry & entry)102 RemoveIdentChange(KVCatalog* catalog, StringData ident, const Entry& entry)
103 : _catalog(catalog), _ident(ident.toString()), _entry(entry) {}
104
commit()105 virtual void commit() {}
rollback()106 virtual void rollback() {
107 stdx::lock_guard<stdx::mutex> lk(_catalog->_identsLock);
108 _catalog->_idents[_ident] = _entry;
109 }
110
111 KVCatalog* const _catalog;
112 const std::string _ident;
113 const Entry _entry;
114 };
115
isFeatureDocument(BSONObj obj)116 bool KVCatalog::FeatureTracker::isFeatureDocument(BSONObj obj) {
117 BSONElement firstElem = obj.firstElement();
118 if (firstElem.fieldNameStringData() == kIsFeatureDocumentFieldName) {
119 return firstElem.booleanSafe();
120 }
121 return false;
122 }
123
isCompatibleWithCurrentCode(OperationContext * opCtx) const124 Status KVCatalog::FeatureTracker::isCompatibleWithCurrentCode(OperationContext* opCtx) const {
125 FeatureBits versionInfo = getInfo(opCtx);
126
127 uint64_t unrecognizedNonRepairableFeatures =
128 versionInfo.nonRepairableFeatures & ~_usedNonRepairableFeaturesMask;
129 if (unrecognizedNonRepairableFeatures) {
130 StringBuilder sb;
131 sb << "The data files use features not recognized by this version of mongod; the NR feature"
132 " bits in positions ";
133 appendPositionsOfBitsSet(unrecognizedNonRepairableFeatures, &sb);
134 sb << " aren't recognized by this version of mongod";
135 return {ErrorCodes::MustUpgrade, sb.str()};
136 }
137
138 uint64_t unrecognizedRepairableFeatures =
139 versionInfo.repairableFeatures & ~_usedRepairableFeaturesMask;
140 if (unrecognizedRepairableFeatures) {
141 StringBuilder sb;
142 sb << "The data files use features not recognized by this version of mongod; the R feature"
143 " bits in positions ";
144 appendPositionsOfBitsSet(unrecognizedRepairableFeatures, &sb);
145 sb << " aren't recognized by this version of mongod";
146 return {ErrorCodes::CanRepairToDowngrade, sb.str()};
147 }
148
149 return Status::OK();
150 }
151
get(OperationContext * opCtx,KVCatalog * catalog,RecordId rid)152 std::unique_ptr<KVCatalog::FeatureTracker> KVCatalog::FeatureTracker::get(OperationContext* opCtx,
153 KVCatalog* catalog,
154 RecordId rid) {
155 auto record = catalog->_rs->dataFor(opCtx, rid);
156 BSONObj obj = record.toBson();
157 invariant(isFeatureDocument(obj));
158 return std::unique_ptr<KVCatalog::FeatureTracker>(new KVCatalog::FeatureTracker(catalog, rid));
159 }
160
create(OperationContext * opCtx,KVCatalog * catalog)161 std::unique_ptr<KVCatalog::FeatureTracker> KVCatalog::FeatureTracker::create(
162 OperationContext* opCtx, KVCatalog* catalog) {
163 return std::unique_ptr<KVCatalog::FeatureTracker>(
164 new KVCatalog::FeatureTracker(catalog, RecordId()));
165 }
166
isNonRepairableFeatureInUse(OperationContext * opCtx,NonRepairableFeature feature) const167 bool KVCatalog::FeatureTracker::isNonRepairableFeatureInUse(OperationContext* opCtx,
168 NonRepairableFeature feature) const {
169 FeatureBits versionInfo = getInfo(opCtx);
170 return versionInfo.nonRepairableFeatures & static_cast<NonRepairableFeatureMask>(feature);
171 }
172
markNonRepairableFeatureAsInUse(OperationContext * opCtx,NonRepairableFeature feature)173 void KVCatalog::FeatureTracker::markNonRepairableFeatureAsInUse(OperationContext* opCtx,
174 NonRepairableFeature feature) {
175 FeatureBits versionInfo = getInfo(opCtx);
176 versionInfo.nonRepairableFeatures |= static_cast<NonRepairableFeatureMask>(feature);
177 putInfo(opCtx, versionInfo);
178 }
179
markNonRepairableFeatureAsNotInUse(OperationContext * opCtx,NonRepairableFeature feature)180 void KVCatalog::FeatureTracker::markNonRepairableFeatureAsNotInUse(OperationContext* opCtx,
181 NonRepairableFeature feature) {
182 FeatureBits versionInfo = getInfo(opCtx);
183 versionInfo.nonRepairableFeatures &= ~static_cast<NonRepairableFeatureMask>(feature);
184 putInfo(opCtx, versionInfo);
185 }
186
isRepairableFeatureInUse(OperationContext * opCtx,RepairableFeature feature) const187 bool KVCatalog::FeatureTracker::isRepairableFeatureInUse(OperationContext* opCtx,
188 RepairableFeature feature) const {
189 FeatureBits versionInfo = getInfo(opCtx);
190 return versionInfo.repairableFeatures & static_cast<RepairableFeatureMask>(feature);
191 }
192
markRepairableFeatureAsInUse(OperationContext * opCtx,RepairableFeature feature)193 void KVCatalog::FeatureTracker::markRepairableFeatureAsInUse(OperationContext* opCtx,
194 RepairableFeature feature) {
195 FeatureBits versionInfo = getInfo(opCtx);
196 versionInfo.repairableFeatures |= static_cast<RepairableFeatureMask>(feature);
197 putInfo(opCtx, versionInfo);
198 }
199
markRepairableFeatureAsNotInUse(OperationContext * opCtx,RepairableFeature feature)200 void KVCatalog::FeatureTracker::markRepairableFeatureAsNotInUse(OperationContext* opCtx,
201 RepairableFeature feature) {
202 FeatureBits versionInfo = getInfo(opCtx);
203 versionInfo.repairableFeatures &= ~static_cast<RepairableFeatureMask>(feature);
204 putInfo(opCtx, versionInfo);
205 }
206
getInfo(OperationContext * opCtx) const207 KVCatalog::FeatureTracker::FeatureBits KVCatalog::FeatureTracker::getInfo(
208 OperationContext* opCtx) const {
209 if (_rid.isNull()) {
210 return {};
211 }
212
213 auto record = _catalog->_rs->dataFor(opCtx, _rid);
214 BSONObj obj = record.toBson();
215 invariant(isFeatureDocument(obj));
216
217 BSONElement nonRepairableFeaturesElem;
218 auto nonRepairableFeaturesStatus = bsonExtractTypedField(
219 obj, kNonRepairableFeaturesFieldName, BSONType::NumberLong, &nonRepairableFeaturesElem);
220 fassert(40111, nonRepairableFeaturesStatus);
221
222 BSONElement repairableFeaturesElem;
223 auto repairableFeaturesStatus = bsonExtractTypedField(
224 obj, kRepairableFeaturesFieldName, BSONType::NumberLong, &repairableFeaturesElem);
225 fassert(40112, repairableFeaturesStatus);
226
227 FeatureBits versionInfo;
228 versionInfo.nonRepairableFeatures =
229 static_cast<NonRepairableFeatureMask>(nonRepairableFeaturesElem.numberLong());
230 versionInfo.repairableFeatures =
231 static_cast<RepairableFeatureMask>(repairableFeaturesElem.numberLong());
232 return versionInfo;
233 }
234
putInfo(OperationContext * opCtx,const FeatureBits & versionInfo)235 void KVCatalog::FeatureTracker::putInfo(OperationContext* opCtx, const FeatureBits& versionInfo) {
236 BSONObjBuilder bob;
237 bob.appendBool(kIsFeatureDocumentFieldName, true);
238 // We intentionally include the "ns" field with a null value in the feature document to prevent
239 // older versions that do 'obj["ns"].String()' from starting up. This way only versions that are
240 // aware of the feature document's existence can successfully start up.
241 bob.appendNull(kNamespaceFieldName);
242 bob.append(kNonRepairableFeaturesFieldName,
243 static_cast<long long>(versionInfo.nonRepairableFeatures));
244 bob.append(kRepairableFeaturesFieldName,
245 static_cast<long long>(versionInfo.repairableFeatures));
246 BSONObj obj = bob.done();
247
248 if (_rid.isNull()) {
249 // This is the first time a feature is being marked as in-use or not in-use, so we must
250 // insert the feature document rather than update it.
251 const bool enforceQuota = false;
252 // TODO SERVER-30638: using timestamp 0 for these inserts
253 auto rid = _catalog->_rs->insertRecord(
254 opCtx, obj.objdata(), obj.objsize(), Timestamp(), enforceQuota);
255 fassert(40113, rid.getStatus());
256 _rid = rid.getValue();
257 } else {
258 const bool enforceQuota = false;
259 UpdateNotifier* notifier = nullptr;
260 auto status = _catalog->_rs->updateRecord(
261 opCtx, _rid, obj.objdata(), obj.objsize(), enforceQuota, notifier);
262 fassert(40114, status);
263 }
264 }
265
KVCatalog(RecordStore * rs,bool directoryPerDb,bool directoryForIndexes)266 KVCatalog::KVCatalog(RecordStore* rs, bool directoryPerDb, bool directoryForIndexes)
267 : _rs(rs),
268 _directoryPerDb(directoryPerDb),
269 _directoryForIndexes(directoryForIndexes),
270 _rand(_newRand()) {}
271
~KVCatalog()272 KVCatalog::~KVCatalog() {
273 _rs = NULL;
274 }
275
_newRand()276 std::string KVCatalog::_newRand() {
277 return str::stream() << std::unique_ptr<SecureRandom>(SecureRandom::create())->nextInt64();
278 }
279
_hasEntryCollidingWithRand() const280 bool KVCatalog::_hasEntryCollidingWithRand() const {
281 // Only called from init() so don't need to lock.
282 for (NSToIdentMap::const_iterator it = _idents.begin(); it != _idents.end(); ++it) {
283 if (StringData(it->first).endsWith(_rand))
284 return true;
285 }
286 return false;
287 }
288
getFilesystemPathForDb(const std::string & dbName) const289 std::string KVCatalog::getFilesystemPathForDb(const std::string& dbName) const {
290 if (_directoryPerDb) {
291 return storageGlobalParams.dbpath + '/' + NamespaceString::escapeDbName(dbName);
292 } else {
293 return storageGlobalParams.dbpath;
294 }
295 }
296
_newUniqueIdent(StringData ns,const char * kind)297 std::string KVCatalog::_newUniqueIdent(StringData ns, const char* kind) {
298 // If this changes to not put _rand at the end, _hasEntryCollidingWithRand will need fixing.
299 StringBuilder buf;
300 if (_directoryPerDb) {
301 buf << NamespaceString::escapeDbName(nsToDatabaseSubstring(ns)) << '/';
302 }
303 buf << kind;
304 buf << (_directoryForIndexes ? '/' : '-');
305 buf << _next.fetchAndAdd(1) << '-' << _rand;
306 return buf.str();
307 }
308
init(OperationContext * opCtx)309 void KVCatalog::init(OperationContext* opCtx) {
310 // No locking needed since called single threaded.
311 auto cursor = _rs->getCursor(opCtx);
312 while (auto record = cursor->next()) {
313 BSONObj obj = record->data.releaseToBson();
314
315 if (FeatureTracker::isFeatureDocument(obj)) {
316 // There should be at most one version document in the catalog.
317 invariant(!_featureTracker);
318
319 // Initialize the feature tracker and skip over the version document because it doesn't
320 // correspond to a namespace entry.
321 _featureTracker = FeatureTracker::get(opCtx, this, record->id);
322 continue;
323 }
324
325 // No rollback since this is just loading already committed data.
326 string ns = obj["ns"].String();
327 string ident = obj["ident"].String();
328 _idents[ns] = Entry(ident, record->id);
329 }
330
331 if (!_featureTracker) {
332 // If there wasn't a feature document, then just an initialize a feature tracker that
333 // doesn't manage a feature document yet.
334 _featureTracker = KVCatalog::FeatureTracker::create(opCtx, this);
335 }
336
337 // In the unlikely event that we have used this _rand before generate a new one.
338 while (_hasEntryCollidingWithRand()) {
339 _rand = _newRand();
340 }
341 }
342
getAllCollections(std::vector<std::string> * out) const343 void KVCatalog::getAllCollections(std::vector<std::string>* out) const {
344 stdx::lock_guard<stdx::mutex> lk(_identsLock);
345 for (NSToIdentMap::const_iterator it = _idents.begin(); it != _idents.end(); ++it) {
346 out->push_back(it->first);
347 }
348 }
349
newCollection(OperationContext * opCtx,StringData ns,const CollectionOptions & options,KVPrefix prefix)350 Status KVCatalog::newCollection(OperationContext* opCtx,
351 StringData ns,
352 const CollectionOptions& options,
353 KVPrefix prefix) {
354 invariant(opCtx->lockState()->isDbLockedForMode(nsToDatabaseSubstring(ns), MODE_X));
355
356 const string ident = _newUniqueIdent(ns, "collection");
357
358 stdx::lock_guard<stdx::mutex> lk(_identsLock);
359 Entry& old = _idents[ns.toString()];
360 if (!old.ident.empty()) {
361 return Status(ErrorCodes::NamespaceExists, "collection already exists");
362 }
363
364 opCtx->recoveryUnit()->registerChange(new AddIdentChange(this, ns));
365
366 BSONObj obj;
367 {
368 BSONObjBuilder b;
369 b.append("ns", ns);
370 b.append("ident", ident);
371 BSONCollectionCatalogEntry::MetaData md;
372 md.ns = ns.toString();
373 md.options = options;
374 md.prefix = prefix;
375 b.append("md", md.toBSON());
376 obj = b.obj();
377 }
378 const bool enforceQuota = false;
379 // TODO SERVER-30638: using timestamp 0 for these inserts.
380 StatusWith<RecordId> res =
381 _rs->insertRecord(opCtx, obj.objdata(), obj.objsize(), Timestamp(), enforceQuota);
382 if (!res.isOK())
383 return res.getStatus();
384
385 old = Entry(ident, res.getValue());
386 LOG(1) << "stored meta data for " << ns << " @ " << res.getValue();
387 return Status::OK();
388 }
389
getCollectionIdent(StringData ns) const390 std::string KVCatalog::getCollectionIdent(StringData ns) const {
391 stdx::lock_guard<stdx::mutex> lk(_identsLock);
392 NSToIdentMap::const_iterator it = _idents.find(ns.toString());
393 invariant(it != _idents.end());
394 return it->second.ident;
395 }
396
getIndexIdent(OperationContext * opCtx,StringData ns,StringData idxName) const397 std::string KVCatalog::getIndexIdent(OperationContext* opCtx,
398 StringData ns,
399 StringData idxName) const {
400 BSONObj obj = _findEntry(opCtx, ns);
401 BSONObj idxIdent = obj["idxIdent"].Obj();
402 return idxIdent[idxName].String();
403 }
404
_findEntry(OperationContext * opCtx,StringData ns,RecordId * out) const405 BSONObj KVCatalog::_findEntry(OperationContext* opCtx, StringData ns, RecordId* out) const {
406 RecordId dl;
407 {
408 stdx::lock_guard<stdx::mutex> lk(_identsLock);
409 NSToIdentMap::const_iterator it = _idents.find(ns.toString());
410 invariant(it != _idents.end());
411 dl = it->second.storedLoc;
412 }
413
414 LOG(3) << "looking up metadata for: " << ns << " @ " << dl;
415 RecordData data;
416 if (!_rs->findRecord(opCtx, dl, &data)) {
417 // since the in memory meta data isn't managed with mvcc
418 // its possible for different transactions to see slightly
419 // different things, which is ok via the locking above.
420 return BSONObj();
421 }
422
423 if (out)
424 *out = dl;
425
426 return data.releaseToBson().getOwned();
427 }
428
getMetaData(OperationContext * opCtx,StringData ns)429 const BSONCollectionCatalogEntry::MetaData KVCatalog::getMetaData(OperationContext* opCtx,
430 StringData ns) {
431 BSONObj obj = _findEntry(opCtx, ns);
432 LOG(3) << " fetched CCE metadata: " << obj;
433 BSONCollectionCatalogEntry::MetaData md;
434 const BSONElement mdElement = obj["md"];
435 if (mdElement.isABSONObj()) {
436 LOG(3) << "returning metadata: " << mdElement;
437 md.parse(mdElement.Obj());
438 }
439 return md;
440 }
441
putMetaData(OperationContext * opCtx,StringData ns,BSONCollectionCatalogEntry::MetaData & md)442 void KVCatalog::putMetaData(OperationContext* opCtx,
443 StringData ns,
444 BSONCollectionCatalogEntry::MetaData& md) {
445 RecordId loc;
446 BSONObj obj = _findEntry(opCtx, ns, &loc);
447
448 {
449 // rebuilt doc
450 BSONObjBuilder b;
451 b.append("md", md.toBSON());
452
453 BSONObjBuilder newIdentMap;
454 BSONObj oldIdentMap;
455 if (obj["idxIdent"].isABSONObj())
456 oldIdentMap = obj["idxIdent"].Obj();
457
458 // fix ident map
459 for (size_t i = 0; i < md.indexes.size(); i++) {
460 string name = md.indexes[i].name();
461 BSONElement e = oldIdentMap[name];
462 if (e.type() == String) {
463 newIdentMap.append(e);
464 continue;
465 }
466 // missing, create new
467 newIdentMap.append(name, _newUniqueIdent(ns, "index"));
468 }
469 b.append("idxIdent", newIdentMap.obj());
470
471 // add whatever is left
472 b.appendElementsUnique(obj);
473 obj = b.obj();
474 }
475
476 LOG(3) << "recording new metadata: " << obj;
477 Status status = _rs->updateRecord(opCtx, loc, obj.objdata(), obj.objsize(), false, NULL);
478 fassert(28521, status.isOK());
479 }
480
renameCollection(OperationContext * opCtx,StringData fromNS,StringData toNS,bool stayTemp)481 Status KVCatalog::renameCollection(OperationContext* opCtx,
482 StringData fromNS,
483 StringData toNS,
484 bool stayTemp) {
485 RecordId loc;
486 BSONObj old = _findEntry(opCtx, fromNS, &loc).getOwned();
487 {
488 BSONObjBuilder b;
489
490 b.append("ns", toNS);
491
492 BSONCollectionCatalogEntry::MetaData md;
493 md.parse(old["md"].Obj());
494 md.rename(toNS);
495 if (!stayTemp)
496 md.options.temp = false;
497 b.append("md", md.toBSON());
498
499 b.appendElementsUnique(old);
500
501 BSONObj obj = b.obj();
502 Status status = _rs->updateRecord(opCtx, loc, obj.objdata(), obj.objsize(), false, NULL);
503 fassert(28522, status.isOK());
504 }
505
506 stdx::lock_guard<stdx::mutex> lk(_identsLock);
507 const NSToIdentMap::iterator fromIt = _idents.find(fromNS.toString());
508 invariant(fromIt != _idents.end());
509
510 opCtx->recoveryUnit()->registerChange(new RemoveIdentChange(this, fromNS, fromIt->second));
511 opCtx->recoveryUnit()->registerChange(new AddIdentChange(this, toNS));
512
513 _idents.erase(fromIt);
514 _idents[toNS.toString()] = Entry(old["ident"].String(), loc);
515
516 return Status::OK();
517 }
518
dropCollection(OperationContext * opCtx,StringData ns)519 Status KVCatalog::dropCollection(OperationContext* opCtx, StringData ns) {
520 invariant(opCtx->lockState()->isDbLockedForMode(nsToDatabaseSubstring(ns), MODE_X));
521 stdx::lock_guard<stdx::mutex> lk(_identsLock);
522 const NSToIdentMap::iterator it = _idents.find(ns.toString());
523 if (it == _idents.end()) {
524 return Status(ErrorCodes::NamespaceNotFound, "collection not found");
525 }
526
527 opCtx->recoveryUnit()->registerChange(new RemoveIdentChange(this, ns, it->second));
528
529 LOG(1) << "deleting metadata for " << ns << " @ " << it->second.storedLoc;
530 _rs->deleteRecord(opCtx, it->second.storedLoc);
531 _idents.erase(it);
532
533 return Status::OK();
534 }
535
getAllIdentsForDB(StringData db) const536 std::vector<std::string> KVCatalog::getAllIdentsForDB(StringData db) const {
537 std::vector<std::string> v;
538
539 {
540 stdx::lock_guard<stdx::mutex> lk(_identsLock);
541 for (NSToIdentMap::const_iterator it = _idents.begin(); it != _idents.end(); ++it) {
542 NamespaceString ns(it->first);
543 if (ns.db() != db)
544 continue;
545 v.push_back(it->second.ident);
546 }
547 }
548
549 return v;
550 }
551
getAllIdents(OperationContext * opCtx) const552 std::vector<std::string> KVCatalog::getAllIdents(OperationContext* opCtx) const {
553 std::vector<std::string> v;
554
555 auto cursor = _rs->getCursor(opCtx);
556 while (auto record = cursor->next()) {
557 BSONObj obj = record->data.releaseToBson();
558 if (FeatureTracker::isFeatureDocument(obj)) {
559 // Skip over the version document because it doesn't correspond to a namespace entry and
560 // therefore doesn't refer to any idents.
561 continue;
562 }
563 v.push_back(obj["ident"].String());
564
565 BSONElement e = obj["idxIdent"];
566 if (!e.isABSONObj())
567 continue;
568 BSONObj idxIdent = e.Obj();
569
570 BSONObjIterator sub(idxIdent);
571 while (sub.more()) {
572 BSONElement e = sub.next();
573 v.push_back(e.String());
574 }
575 }
576
577 return v;
578 }
579
isUserDataIdent(StringData ident) const580 bool KVCatalog::isUserDataIdent(StringData ident) const {
581 return ident.find("index-") != std::string::npos || ident.find("index/") != std::string::npos ||
582 ident.find("collection-") != std::string::npos ||
583 ident.find("collection/") != std::string::npos;
584 }
585 }
586