Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
Two client-side rules refused documents the server stores on insert.
mongoc_collection_insert_one, andscriptInsertcallsmongoc_collection_insert_many.scriptInsertis behind every shellinsertOne,insertMany,insertandsave, and therefore behind every grid new row, duplicate, paste and undo-delete restore. Both calls passed no options. libmongoc 1.28.1 then applies_mongoc_default_insert_vflags, which isBSON_VALIDATE_UTF8 | BSON_VALIDATE_UTF8_ALLOW_NULL | BSON_VALIDATE_EMPTY_KEYS. It refuses a field named""at any depth with[22] invalid document for insert: empty keybefore anything is sent. The server has no such rule. MongoDB 7.0.43 stores the field through the raw insert command and through mongosh, and TablePro's ownbulkWrite, which sends a raw command, already stored it.MongoDocumentTextrefused a top-level name starting with$as something "MongoDB reads as an operator". That is true of a replacement, not of an insert. MongoDB 5.0 and later stores the name as written: insert.cpp r5.0.0 gates the old refusal behindfeatureFlagDotsAndDollars, which is on by default. Servers before 5.0 refuse it themselves with BadValue. So Insert Document refused a document that its owninsertOnestatement stores when run in the query editor.Fix
MongoInsertOptions, compiled into TableProTests. It holds the options every insert hands libmongoc:{"validate":9}. That is libmongoc's default check withoutBSON_VALIDATE_EMPTY_KEYS, so a key that is not UTF-8 is still refused. The value is neverfalseor 0, which turns every check off.insertDocumentandscriptInsertparse that one text and pass it toinsert_oneandinsert_many.$check inMongoDocumentText.init(parsing:)is gone, along withRefusal.operatorFieldand its catalog string. Nothing else referenced them.findOneAndUpdate,findOneAndReplaceandfindOneAndDeletereturn, from its fields, never as one value. A document whose only field is named$oid,$dateor another Extended JSON wrapper, which this change lets Insert Document store, used to come back fromfind({}, {_id: 0})as that value, or fail when the text was not a valid one.databases/mongodb.mdx):$names.$refplus$idas a DBRef.$getField/$setFieldas the workaround.$half gets no Fixed entry.Verified
verify.sh test, 8 suites (MongoInsertOptionsTests, MongoDocumentTextTests, MongoDocumentWritePlanTests, MongoDBStatementGeneratorTests, MongoDBWriteBackTypeTests, MongoScriptCommandBuilderTests, MongoScriptCursorOptionsTests, MongoWriteFailureTests): 132 executed, 132 passed.$refusal back in the reader: 30 executed, 3 failed, namelydropsOnlyTheEmptyNameCheck,namesAreReadAsWrittenandnamesTheServerStores.MongoInsertOptionsTestsdoes not compile on main.verify.sh build MongoDBDriver,plugins(AllPlugins),build(TablePro): all PASS.$names the 5.0 caveat when dotted names share it. Both fixed.wrapperNamedDocumentStaysADocumentandfindAndModifyDocumentStaysADocumentfail against main's prelude and pass here.mainatc21dc512e: MongoScriptPreludeTests, MongoDocumentTextTests, MongoDocumentWritePlanTests, MongoInsertOptionsTests and StringCatalogIntegrityTests, 64 of 64. MongoDBDriver and the app build.verify.sh linton the changed Swift files: 0 violations.verify.sh docs: PASS.localization.py pluginsandverify: ok.{"_id": {"$foo": 1}}, refused by the server with[52] _id fields may not contain '$'-prefixed fields, and{"": 1, "": 2}, refused by the reader as a repeated field.""field stored on main (onlybulkWrite), 7 of 7 on the branch. Cases:insertOneat top level, nested and inside an array element,insertMany, legacyinsert, andsavewithout_id.db.g.insertOne({"": "blank", "a": {"": 2}, "name": "n"})and its undo-delete restore with_id7: both[22] ... empty keyon main, both stored on the branch.[_id] [] [a] [b] [$foo] [a.b] [$set] [ ] [$oid] [$date] [$numberInt] [$id] [$ref].Composition with work in flight
Edit Document (#3152). Its replacement always starts with the stored
_id. I measured on 7.0.43 with{"validate": false}:{"_id": 1, "$set": {"x": 1}}is refused by the server with code 52, and the stored document is unchanged.$setis applied as an update:{"$set": {"x": 1}}turned{_id: 1, orig: 1}into{_id: 1, orig: 1, x: 1}. Edit Document cannot build that shape.So removing the reader's refusal opens no silent write. What Edit Document loses is its open-time refusal: a document with a top-level
$field would open in the editor and then fail every save with code 52. After rebasing onto this, that branch needs:MongoDocumentReplacementTests.topLevelOperator,MongoEditableDocumentTests(line 84) and its copy ofMongoDocumentTextTests.operatorFieldsfixed, since all three referenceRefusal.operatorFieldand will not compile.$refusal inMongoDocumentReplacement, next toemptyTimestampField, applied at save and whenMongoEditableDocumentopens a stored document, with a new string added throughlocalization.py plugins --add.validate9 in place offalse. Measured, it refuses$setclient-side withInvalid key '$set': replace prohibits $ operatorsand still stores{"_id": 1, "": 1, "a": {"": 2}}.MongoDocumentWritePlanTests.namesTheServerStoresrewritten againstplan?.write == .insert(document:), since that branch replacesplan.documentand makesmakereturn an optional.MongoDBConnection+Documents.swiftwith itsreplaceDocumentand read code. My hunk there is the options parse insideinsertDocument.Shell write concern (#3151). Both branches change
scriptInsert'sinsert_manyoptions. libmongoc applies its EMPTY_KEYS default to any options document with novalidatekey. So itsinsertOptions(statementOptions:)must always return a document carrying"validate": MongoInsertOptions.validationbesidewriteConcernandordered, never nil, with a test that says so.Grid write values (
fix/mongodb-grid-write-values) and null-vs-missing on top of it. The generator's insert refusal of a""field existed only because of libmongoc's default. It can drop to__proto__only, andinsertRefusesUnwritableNamesshould change to match. Its$setFieldedits also make the editing half of the new Limitations bullet stale for$and dotted names, so its docs change should update that bullet.Deliberately not fixed here
Grid filter, sort and edit on special names, measured the same on main and here:
"": filter and sort are refused by libmongoc's find ([16] Invalid filter: empty key,[16] Invalid opts: empty key), and an edit gets server code 56.$: the server refuses filter (2), sort (16410) and edit (52).{"$set": {"a.b": "edited"}}changedbinsideaand left the literala.bfield alone.All of this predates the change, since the shell and Insert Document already stored dotted names. It is now documented as a limitation. The edit half belongs to the grid write values branch.
A top-level
$refand$idpair is stored as written. mongosh reads that document back as a DBRef, while TablePro shows two columns. It is kept for parity, since mongosh writes the same document, and the docs say so.MongoDBConnection.insertOne/updateOne/deleteOne(database:collection:...)and their*Synchelpers have no callers and still pass no options. Removing them is a separate refactor.The shell sends
replaceOne(filter, {$set: ...})as a raw update command, so the server applies it as an update, while mongosh refuses it. That belongs to the shell writes work.A document nested inside another whose only field is named like an Extended JSON wrapper still reads back in the query editor as that wrapper's value. The driver hands documents to JavaScript as Extended JSON, where the two spell the same, so telling them apart needs the BSON type carried alongside, which is its own change. The root of every returned document is fixed here.
Servers before 5.0 are covered by the insert.cpp source only (r3.6.23 to r4.4.29), not measured live.
No UI test: CI has no MongoDB server, and Insert Document, the query editor insert and the grid insert all need a connected collection. Coverage is the unit tests above plus the live before and after run.