fix(plugin-mongodb): store shell numbers past 2^53 as the doubles they are, define every constructor autocomplete offers, and write stored regexes and decimals back unchanged - #3158
Open
datlechin wants to merge 1 commit into
Conversation
…y are, define every constructor autocomplete offers, and write stored regexes and decimals back unchanged
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 has not been 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.
Stacked on #3150, which adds
Double(),BSONRegExp(),BSONSymbol()and__doubleTextto the same prelude block. Retarget tomainonce #3150 merges.Root cause
Three separate defects in the shell prelude (
MongoScriptPrelude.swift), all measured against libbson 1.28.1, MongoDB 7.0.43 and mongosh 2.10.0:Numbers.
serializeNumbersent every whole number outside int32 as{"$numberLong": String(value)}.String(value)is JavaScript's shortest round-trip spelling, not an integer:1e20went out as"100000000000000000000"and2^63as"9223372036854776000", both of which libbson refuses, soinsertOne({n: 1e20})failed with "This is not a document MongoDB can read".2**62went out as"4611686018427388000", which is a different integer.-0went out as int320.NumberLong(2**62)had the same problem.Constructors. Autocomplete offered
Int32,Long,Decimal128andBSONRegExp, and the prelude defined none of them, so each ended in aReferenceError. Several other constructors misbehaved:Timestamp({t, i})produced a nested timestamp libbson refuses.Timestamp(4294967296, 0)was stored as t = 0.NumberIntusedparseInt, soNumberInt("12abc")stored 12 andNumberInt(0.0000005)stored 5.NumberDecimal("NaN")threw.Read-back. A cursor revived
$numberDecimalthroughNumberDecimal, so a stored decimalNaNthrew. It revived$regularExpressionthroughnew RegExp, so a storedxorloption or PCRE-only syntax threwSyntaxErrorpartway throughforEach. A regex JavaScript could compile was written back assource, which escapes/and spells an empty pattern(?:).Fix
Number.MAX_SAFE_INTEGERas int64, as before.-0,NaN,Infinityand fractions are sent as doubles.Int32/NumberInt,Long/NumberLong,Decimal128/NumberDecimal. Each alias pair shares one prototype, soinstanceofworks either way round.Longgoes throughBigInt. It is exact from a string, aLongor a bigint. From a number it gives the integer the script holds, soNumberLong(2**62)is4611686018427387904, as in mongosh.Long(low, high)composes two 32-bit halves.Timestamp.(t, i),(t),(), a{t, i}object whose parts are numbers, aLongor a bigint.EJSON.parseorEJSON.deserializegoes through the same checks as the constructors, so{"$numberLong": "abc"}, an out-of-range$numberInt, a$numberDecimalor$numberDoublethat is not a number, and a$regularExpressionwith an option outsideilmsuxthrow there, as they do in mongosh, instead of reaching libbson later as a document it cannot read.RegExpthat carries the server's pattern and options. When JavaScript cannot compile it, it revives as aBSONRegExp. Either way it writes back unchanged.MinKey,MaxKey,Code,DBRef,BSONSymboland the legacy UUID helpers, and a test pins the two sets as equal.===and==give on values read back. A new Limitations line covers Decimal128 precision and exponent range, each with its own remedy.Verified
MongoShellValueTests(7 cases) andMongoVocabularyConstructorTests(1 case). The unchanged prelude fails each of them.TablePro,MongoDBDriverandAllPluginsall pass.insertOne({a: 1e20, b: 9223372036854775807, c: -0, d: 2147483648, e: 5, f: 9007199254740993, j: 9007199254740991})stored double, double, double (-0), long, int, double and long. mongosh stores the same values the same way exceptdandj, which it stores as doubles.find().forEach(d => replaceOne({_id: d._id}, d))over a document holding a decimalNaNandx,l,(?i),a/band empty regexes ran without error.$regexFindon the server confirmed that thexoption and the(?i)pattern survived the write-back.SyntaxError: Invalid regular expression: /(?i)a/: Invalid group.Long(5, 1)as 4294967301,Timestamp(NumberLong("8589934593"))as {t: 2, i: 1] andBSONRegExp("^a", "mi")with optionsim.Timestamp(4294967296, 0)throwsTimestamp takes t from 0 to 4294967295before anything is sent.EJSON.parseaccepted malformed wrappers once revival skipped the constructors, and a P3 that the Decimal128 limitation gave no remedy for an exponent out of range. Both fixed.ejsonParseChecksWrappers(7 cases) andejsonParseReadsWellFormedWrappersfail against the round-0 prelude;serverRepliesAreNotRecheckedpins that a stored legacy regex option still reads back.aaf1c9545): MongoScriptPreludeTests 34 of 34, MongoVocabularyConstructorTests, MongoShellValueTests and the rest 41 of 41. The app andMongoDBDriverbuild, lint reports 0 violations, docs checks pass.NumberLong("123456789012")calls take 964 ms against 557 ms before, which is the cost of range-checking what the user types.Deliberately not fixed here
{$type: "binData"}and{$regex, $options}: that is fix(plugin-mongodb): send $type, $regex and $options objects as the operator documents they are written as #3156.Double()keeps a double.NaNand a regex as canonical wrappers rather thanNumberDecimal("NaN")andBSONRegExp(...).No UI test: every path needs a connected MongoDB server, and CI has none. The prelude is covered in-process through
MongoScriptContext, and the server side by the live checks listed here.