Conversation
…tions, and add db.createView to the shell
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
This was referenced Sep 26, 2026
Open
datlechin
added this pull request to stack #3163
September 26, 2026 19:23
datlechin
removed this pull request from stack #3163
September 26, 2026 19:53
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
Views listed as tables.
fetchTablesusedmongoc_database_get_collection_names_with_opts. In libmongoc 1.28.1 that call keeps each listCollections entry'snameand throws itstypeaway. Every name then becamePluginTableInfo(type: "table"), so a view was a table everywhere in the app:system.*collections were plain tables the same way. Three things were also broken that only showed once views were reported correctly:fetchIndexesthrew 166 into the Structure tab's error banner.fetchTableDDLreturned only// Collection: adults.fetchViewDefinitionthrewunsupportedOperation.The shell prelude had no
DB.prototype.createView, sodb.createViewresolved to a collection namedcreateView. The New View template failed withTypeError: db.createView is not a function.Index order and options lost.
listIndexesdocuments went throughJSONSerializationinto a[String: Any], so the key document lost its order before the driver saw it. As a result:fetchIndexesreportedArray(key.keys)in hash order and hard-codedBTREE.fetchTableDDLwrote the key sorted alphabetically.Int32does not cast toInt), and dropped partial filters, collations, weights and the rest.sizehit the sameInt32cast and printedsize: 0.Separately, the shell's own
createIndexpassed options through a fixed allowlist rebuilt from a Swift dictionary. So even correct DDL would losewildcardProjection, a 2d index'sbits/min/max, and the text-index options when run in a query tab.Value types lost in generated statements. A bare number in a statement reaches the shell as a JavaScript
Number, which the shell sends as an Int32 or Int64 whatever type the server held. SoNumberLong(1)and a whole Double such as1.0came back as Int32s,9007199254740993came back as9007199254740992, and{"$minKey": 1}and a$timestamp'st/ibecame$numberIntwrappers the server cannot read.Wrappers mongosh reads as documents. A regular expression, a Decimal128
NaNor infinity, a symbol and a date outside years 1 to 9999 kept their canonical Extended JSON wrapper. TablePro's host parses that text with libbson and gets the BSON value back, but mongosh sends the object as it is: measured on mongosh 2.10.0, the view DDL failed withunknown operator: $regularExpressionand a validator's with$regex has to be a string.__proto__members lost. A member named__proto__written as a plain key, quoted or not, sets the object's prototype in JavaScript and adds no member. The prelude'sEJSON.serializeandEJSON.deserializecopied members by assignment, which has the same effect. So:$matchor a partial filter on__proto__came back empty from Show DDL, measured in TablePro's shell; in mongosh 2.10.0,{ $match: { "__proto__": { $eq: 1 } } }stored{ $match: {} };__proto__came back keyed on the remaining fields only;__proto__field lost the field.Names from the server ran as statements. Show DDL wrote a server-chosen name into a
//comment as it was:// Collection: <name>before this branch, and// View: <name>on it. MongoDB accepts a line feed, a carriage return, U+2028 and U+2029 in a collection or view name, and JavaScript ends a//comment at each of them, so the rest of the name ran as a statement of its own when the DDL was run from a query tab. v0.75.0 also wrote thecollModname and the index name into string literals with no escaping at all.Edit View Definition's fallback, reachable now that views are views, had two more:
--comments, which JavaScript reads as the decrement operator, and it split the error only at a line feed, so a carriage return or U+2028 in the message ended the comment partway through a view's name.String literals had a quieter gap.
MongoScriptJson.jsonStringand the literal writer escaped only the C0 controls, and libbson leaves U+2028, U+2029 and U+0085 raw in canonical Extended JSON. JavaScript reads those inside a string, but the editor's statement scanner ends a string at anyCharacter.isNewline, so it could split a statement in the middle of a name. PluginKit'sMongoCollectionAccessortests a name byCharacter, andCharacter.isLetterreads only a grapheme's first scalar, so punctuation sharing a grapheme with a letter passed as an identifier.Fix
The server's catalog documents stay canonical Extended JSON text, in server order, and are never rebuilt from a dictionary. They become shell text only when a statement is written, every value through a constructor TablePro's shell and mongosh both define, and every server-chosen string escaped for the position it lands in.
Connection.
listIndexesdrains through the shell's canonical cursor reader instead of a dictionary.listNamespaces(database:named:)readsmongoc_database_find_collections_with_opts, withnameOnlyfor the full list or a name filter for one entry.Pure model, unit-tested without a server.
MongoDBNamespaceEntrymapsviewto VIEW,system.*to SYSTEM TABLE, and everything else (including time series) to TABLE. It buildsdb.createView(...)and the in-placecollMod.MongoDBIndexEntrykeeps key order. It types the index from its key (BTREE, HASH, FULLTEXT, SPATIAL, 2D, WILDCARD) and lists a text index's weighted fields. It writescreateIndexwith every option exceptv,keyandns.MongoDBShellLiteralwrites canonical Extended JSON as shell source:NumberLong("..."), andDouble(1.0)for every whole Double,-0.0and1e+20included;InfinityandNaN;NumberDecimal("...")for every Decimal128,NaNand the infinities included;ISODate("...")for years 1 to 9999 in the proleptic Gregorian calendar JavaScript uses, andnew Date(<ms>)for any other date a JavaScriptDateholds;BSONRegExp("pattern", "options"),BSONSymbol("..."),ObjectId,BinData,Timestamp(t, i),MinKey(),MaxKey()andCode.Only a DBPointer,
undefinedand a date more than 100 million days from 1970 keep their wrapper: mongosh has no way to write any of them, and TablePro's shell sends the wrapper to the server as it is. The index key and options, the view pipeline and collation, the validator and the time-series line are all written this way.MongoDBJsonLayout.shellObjectwrites every shell object literal, with__proto__as the computed key["__proto__"], which JavaScript adds as a member like any other.MongoDBJsonLayout.indentedkeeps a computed key andnew Date(...)whole, and a constructor call on one line.MongoDBNamespaceDDLrenders the DDL. A view's header is// View:, so MQL export's// Collection:scraper never appends acreateViewafter a view's documents.Collations drop the server's ICU
version, so the text also runs on a server with a different ICU build.Escaping for each position.
MongoScriptJson.jsonStringescapes every C0 and C1 control, U+2028 and U+2029.MongoDBShellLiteralwrites each string again through it instead of copying libbson's text, and writes anything that is not a string, a number,true,falseornullas the string it spells.MongoDBShellText.commentwrites every DDL comment line with those same characters as escapes.MongoDBShellText.collectionwritesdb.<name>only when every scalar is an identifier character and the name is not adbmember, anddb.getCollection("<name>")otherwise. The plugin keeps its own check rather than fixing PluginKit's, so a plugin release covers apps that already shipped.MongoDBShellText.memberName, the computed key above.MongoDBObjectStatementswrites Drop, Truncate and the Edit View Definition fallback template, each naming the collection throughMongoScriptJson.jsonString, and the driver returns them.Driver.
fetchTablesuses the entry types.fetchIndexestreats 166 as no indexes, as libmongoc already does for a missing collection.fetchTableDDLreads one entry and renders it.fetchViewDefinitionreturns thecollModthat Edit View Definition runs, orNo view named %@ in this database.prettyJsonand theTableProNumberFormattingimport are gone.supportsRenameView = false: renameCollection on a view is refused with 166.Shell.
DB.prototype.createViewsendscreatewithviewOnandpipeline.createIndexputs every option into the spec verbatim, in written order.commitQuorum,comment,maxTimeMSandwriteConcerngo on the command instead, because the server refusesmaxTimeMSinside a spec.Double(),BSONRegExp()andBSONSymbol()are new. Autocomplete already offered the first two. A wholeDoubleand-0.0stay Doubles.BSONRegExpis the same definition the shell-number PR stacked on this one adds.NumberDecimaltakesNaN,Infinityand-Infinity, the spellings the server writes.EJSON.serialize,EJSON.deserialize,createCollectionandcreateViewdefine a member named__proto__instead of assigning it.App.
viewDefinitionFallbacktakes the template and the tab's line comment marker fromEditorLanguage.lineCommentMarker, so a MongoDB tab gets//. It splits the error at everyCharacter.isNewline, and a language with no line comment gets the template alone.Also here:
MongoScriptJsondecodes JSON escapes in member names. This hunk is byte-identical to Can't create a database collection from the visual editor #3131's.// Collection:header,collModname and index name.No PluginKit change, no kit bump. MongoDB is registry-only, so users get this with a plugin-mongodb release.
Verified
Codex review, three rounds, each fixed on this commit.
NumberLong(1)and a whole Double came back Int32s and9007199254740993came back9007199254740992.// View:comment and ran as a statement.__proto__member became a prototype; [P2] regex, special Decimal128, far date and symbol wrappers reach mongosh as documents; [P2] the fallback wrote SQL--comments into a JavaScript tab.Against this branch's previous commit. I put the previous commit's behaviour behind the new names (its prelude, literal writer, layout, entries, the hand-made escapes for Drop, Truncate and the template, and the old fallback body) and ran the new and changed suites: 45 executed, 24 passed, 21 failed. The 21 failures cover every round 3 finding: Drop, Truncate and fallback-template containment,
__proto__in the literal writer, the layout and the prelude (sent, read back and written again, and as acreateViewoption), the catalog round trip with__proto__, a symbol and far dates added, the no-wrapper check,BSONRegExp/BSONSymbol/NumberDecimal("NaN"),new Date, and the three fallback-comment cases. The three new cases that passed there pin behaviour that did not change: a date past a JavaScriptDate, a DBPointer andundefinedkeep their wrapper, and each editor language's comment marker. On that code Drop sentv\ndb.probe.drop()for a collection namedv\r\ndb.probe.drop(), and a name holding a quote and a combining mark sent a drop ofprobe. The fix was restored byte-identical (cmpof the diff and of the new files).Tests. 52 suites (every MongoDB suite, MQL export helpers,
JavaScriptStatementScannerTests,EditorLanguageTests,PluginTableKindDecoderTests,ObjectRenameEligibilityTests,DatabaseTreeMenuSpecTestsand the newViewDefinitionFallbackTestsandMongoScriptPreludeMemberTests): 884 executed, 884 passed.MongoDBSrvHostTestsran 0 cases because it sits behind#if canImport(CLibMongoc).Builds and checks.
MongoDBDriver,AllPlugins(all 40) and the app build. Lint: 0 violations on the 30 changed Swift files. Docs check passes. Every plugin string is in the catalog.git merge-treeis clean against currentmain(c21dc51) and against #3131's branch (3da4cfd).Live, MongoDB 7.0.43 and mongosh 2.10.0. A harness links the real plugin sources against libmongoc and splits each text with the app's
JavaScriptStatementScanner, running each statement through the driver the way a query tab does. Comparisons are canonical Extended JSON from libbson, in a probe database dropped afterwards.$matchholdsBSONRegExp("(?i)x\/y # z", "ix"),BSONRegExp("^a.b", "s"), a symbol, Decimal128NaNand-Infinity, dates in year 0 and year 10000, and__proto__at two depths; a collection with a validator of{email: {$regex: /@/i}, __proto__: {$exists: false}}, an index keyed on__proto__and a partial filter on it.__proto__dropped andnested: {}, the index keyed onaalone and the partial filter empty. In mongosh the view failed withunknown operator: $regularExpressionand the validator with$regex has to be a string.collMod, in TablePro and in mongosh. The indexes are identical in both. The collection entry is identical in mongosh apart from thevalidationLevelandvalidationActionthe server adds on anycollModthat sets a validator. TablePro refuses the validator statement, which is the$regexlimitation under "Deliberately not fixed here".collMod, and the indexes afterdropIndexesplus the DDL, in TablePro and in mongosh.undefinedand a date 9e15 ms from 1970: identical after its Show DDL in TablePro; mongosh refuses it withunknown operator: $dbPointer.v\"}); db.probe.drop(); //, run from a tab: the previous commit droppedprobe; this one runs as a singlecollMod, the server answers that no such view exists, andprobestays.c\r\nxbesidec\nx: the previous commit droppedc\nx; this one dropsc\r\nx. Truncate of a collection named with a quote and a combining mark, run from a tab: the previous commit droppedprobe; this one runs as a singledeleteManyandprobestays.Deliberately not fixed here
$regexholding a regular expression.{email: {$regex: /@/i}}is refused by TablePro's shell as a document MongoDB cannot read, on the previous commit and on this one: libbson 1.28's JSON reader takes a$regexkey as the legacy wrapper.{email: /@/i}and{email: {$regex: "@", $options: "i"}}both run. A validator written the first way therefore shows in Show DDL as the server holds it, which runs in mongosh and not in a query tab. The fix is in the host's JSON-to-BSON path, which every shell statement uses. The docs page names the limitation.__proto__in other generated text.MongoDBQueryBuilder(filters),MongoDBStatementGenerator(row edits, Can't create a database collection from the visual editor #3131's) and MQL export still write a__proto__member as a plain key, so a filter or an edit on that field reaches the server without it. Found by reading; the mechanism is the one measured above. Each emitter needsmemberName, and each is its own feature.Characterand turns\r\ninto\u000a, so Rename ofr\r\nxrenamedr\nxin the live run. fix(plugin-mongodb): create collections from New Table with their fields as a validator, read the fields back, and write values in their declared types #3145 onmainalready made that escaper scalar-wise. Changing it here conflicted with that change, so the merge takesmain's.// Database:and// Collection:headers throughPluginExportUtilities.sanitizeForSQLComment, which replaces a line feed and a carriage return but keeps U+2028 and U+2029 (MQLExportPlugin.swift:73and:90). Found by reading, not run through mongosh.MongoCollectionAccessorkeeps itsCharactertest, and itsescapeJSONStringleaves U+2028, U+2029 and U+0085 raw. A PluginKit fix reaches plugins only with an app release, which is why the DDL no longer depends on it.declaredSchemastill readslistCollectionsthroughrunCommandJson. Moving it ontolistNamespaces(named:)needs this branch rebased ontomainfirst.MongoDBIndexKindmust stay the inverse of fix(plugin-mongodb): create collections from New Table with their fields as a validator, read the fields back, and write values in their declared types #3145'sindexKeyValue(for:): 2D and WILDCARD read back, but reach its modifyIndex refusal as "MongoDB has no 2D index."MongoScriptJson.readStringand feat(plugin-mongodb): insert MongoDB documents written as Extended JSON #3140'sMongoDocumentTextshould become one.serializeNumbersends a whole number at or above 2^63 as an overflowing$numberLong,EJSON.deserializerevives a whole Double as a plain number, and autocomplete offersInt32,LongandDecimal128, which the shell does not define. The shell-number PR stacked on this one covers them.system.*are refused with 73 onsystem.viewsandsystem.profile, but succeed onsystem.buckets.*. Drop is still offered, and droppingsystem.viewsdeletes every view.system.jsloses Truncate althoughdeleteManyworks there. Each is a separate decision..tableuntil the view is reopened from the sidebar.get_view_definitionnow returns thecollMod.fetchTableMetadataandfetchDatabaseMetadatastill cast$numberIntstats toInt64/Intand get nil.No UI test: every behaviour here needs a live
listCollectionsandlistIndexesfrom a MongoDB server, and CI has none. The app-side gating a VIEW or SYSTEM TABLE row drives is covered byPluginTableKindDecoderTests,ObjectRenameEligibilityTestsandDatabaseTreeMenuSpecTests, and the fallback text byViewDefinitionFallbackTests. The round-trip and containment tests drive the real shell prelude in-process and split the text with the app's own statement scanner.