Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(NODE-4877): Add support for useBigInt64 #3519

Merged
merged 33 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d54383d
feat(NODE-4950)!: remove bson-ext import and squash commits
nbbeeken Jan 13, 2023
6eea5be
Merge branch 'main' into NODE-4877/add-support-for-BSON-useBigInt64-flag
W-A-James Feb 8, 2023
9f623a7
ci(NODE-4877): Fix generate_evergreen_tasks.js
W-A-James Feb 8, 2023
ddda8f8
test(NODE-4877): Work on resolving review comments
W-A-James Feb 15, 2023
6ac4651
test(NODE-4877): Change test name
W-A-James Feb 15, 2023
330a27a
test(NODE-4877): Add new test
W-A-James Feb 15, 2023
90a5cea
test(NODE-4877): add new tests
W-A-James Feb 16, 2023
2e40c2b
test(NODE-4877): Update tests
W-A-James Feb 17, 2023
cde8f0a
fix(NODE-4877): Add validation for bson options
W-A-James Feb 17, 2023
bbf39e8
fix(NODE-4877): revert unneeded change
W-A-James Feb 17, 2023
6b7ed65
fix(NODE-4877): Revert unneeded change
W-A-James Feb 17, 2023
6ca31e8
style(NODE-4877): eslint
W-A-James Feb 17, 2023
ddcc45b
Merge branch 'main' into NODE-4877/add-support-for-BSON-useBigInt64-flag
W-A-James Feb 17, 2023
16153b3
test(NODE-4877): Exclude tests from single servers
W-A-James Feb 17, 2023
733ce4a
fix(NODE-4877): Undo unneeded change
W-A-James Feb 17, 2023
c5c94ae
style(NODE-4877): Revert unneeded style change
W-A-James Feb 21, 2023
c7ad82c
fix(NODE-4877): Revert unneeded change to unused function
W-A-James Feb 21, 2023
f29d8f5
chore(NODE-4877): Bump bson version
W-A-James Feb 21, 2023
e5b0430
test(NODE-4877): Make test more idiomatic
W-A-James Feb 21, 2023
5073ce3
fix(NODE-4877): Add bigint to mongo IntegerType and NestedPaths
W-A-James Feb 21, 2023
4f50116
test(NODE-4877): Remove unneeded NOTE comment
W-A-James Feb 21, 2023
35c7a2c
test(NODE-4877): Add tests to check that BSONErrors get thrown
W-A-James Feb 21, 2023
01447c7
fix(NODE-4877): Add check for existence of options.promoteLongs and o…
W-A-James Feb 21, 2023
7e903a3
fix(NODE-4877): fix condition
W-A-James Feb 21, 2023
f178c82
test(NODE-4877): remove setting promoteValues and promoteLongs
W-A-James Feb 21, 2023
61e1370
test(NODE-4877): Remove explicit calls to connect
W-A-James Feb 21, 2023
efcf0b9
style(NODE-4877): eslint
W-A-James Feb 21, 2023
3485472
Update test/integration/change-streams/change_stream.test.ts
W-A-James Feb 22, 2023
3e3aef3
test(NODE-4877): test reorganization
W-A-James Feb 22, 2023
f33339f
test(NODE-4877): Add operation-level option superceding tests
W-A-James Feb 22, 2023
c8c0b36
test(NODE-4877): Add type annotations
W-A-James Feb 22, 2023
e4dd63a
Merge branch 'main' into NODE-4877/add-support-for-BSON-useBigInt64-flag
W-A-James Feb 22, 2023
4e3469d
test(NODE-4877): remove unneeded import
W-A-James Feb 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,4 @@ etc/docs/build
!docs/**/*.png
!docs/**/*.css
!docs/**/*.js
.nvmrc
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"email": "[email protected]"
},
"dependencies": {
"bson": "^5.0.0",
"bson": "^5.0.1",
"mongodb-connection-string-url": "^2.6.0",
"socks": "^2.7.1"
},
Expand Down
4 changes: 3 additions & 1 deletion src/bson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export interface BSONSerializeOptions
| 'allowObjectSmallerThanBufferSize'
| 'index'
| 'validation'
| 'useBigInt64'
> {
/**
* Enabling the raw option will return a [Node.js Buffer](https://nodejs.org/api/buffer.html)
Expand Down Expand Up @@ -67,6 +66,7 @@ export interface BSONSerializeOptions
export function pluckBSONSerializeOptions(options: BSONSerializeOptions): BSONSerializeOptions {
const {
fieldsAsRaw,
useBigInt64,
promoteValues,
promoteBuffers,
promoteLongs,
Expand All @@ -78,6 +78,7 @@ export function pluckBSONSerializeOptions(options: BSONSerializeOptions): BSONSe
} = options;
return {
fieldsAsRaw,
useBigInt64,
promoteValues,
promoteBuffers,
promoteLongs,
Expand All @@ -102,6 +103,7 @@ export function resolveBSONOptions(
const parentOptions = parent?.bsonOptions;
return {
raw: options?.raw ?? parentOptions?.raw ?? false,
useBigInt64: options?.useBigInt64 ?? parentOptions?.useBigInt64 ?? false,
promoteLongs: options?.promoteLongs ?? parentOptions?.promoteLongs ?? true,
promoteValues: options?.promoteValues ?? parentOptions?.promoteValues ?? true,
promoteBuffers: options?.promoteBuffers ?? parentOptions?.promoteBuffers ?? false,
Expand Down
1 change: 1 addition & 0 deletions src/cmap/auth/mongodb_aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const AWS_RELATIVE_URI = 'http://169.254.170.2';
const AWS_EC2_URI = 'http://169.254.169.254';
const AWS_EC2_PATH = '/latest/meta-data/iam/security-credentials';
const bsonOptions: BSONSerializeOptions = {
useBigInt64: false,
promoteLongs: true,
promoteValues: true,
promoteBuffers: false,
Expand Down
10 changes: 10 additions & 0 deletions src/cmap/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ export class Response {
queryFailure?: boolean;
shardConfigStale?: boolean;
awaitCapable?: boolean;
useBigInt64: boolean;
promoteLongs: boolean;
promoteValues: boolean;
promoteBuffers: boolean;
Expand All @@ -320,6 +321,7 @@ export class Response {
this.raw = message;
this.data = msgBody;
this.opts = opts ?? {
useBigInt64: false,
promoteLongs: true,
promoteValues: true,
promoteBuffers: false,
Expand All @@ -334,6 +336,7 @@ export class Response {
this.fromCompressed = msgHeader.fromCompressed;

// Flag values
this.useBigInt64 = typeof this.opts.useBigInt64 === 'boolean' ? this.opts.useBigInt64 : false;
this.promoteLongs = typeof this.opts.promoteLongs === 'boolean' ? this.opts.promoteLongs : true;
this.promoteValues =
typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
Expand All @@ -354,6 +357,7 @@ export class Response {
// Allow the return of raw documents instead of parsing
const raw = options.raw || false;
const documentsReturnedIn = options.documentsReturnedIn || null;
const useBigInt64 = options.useBigInt64 ?? this.opts.useBigInt64;
const promoteLongs = options.promoteLongs ?? this.opts.promoteLongs;
const promoteValues = options.promoteValues ?? this.opts.promoteValues;
const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers;
Expand All @@ -362,6 +366,7 @@ export class Response {

// Set up the options
const _options: BSONSerializeOptions = {
useBigInt64,
promoteLongs,
promoteValues,
promoteBuffers,
Expand Down Expand Up @@ -590,6 +595,7 @@ export class BinMsg {
checksumPresent: boolean;
moreToCome: boolean;
exhaustAllowed: boolean;
useBigInt64: boolean;
promoteLongs: boolean;
promoteValues: boolean;
promoteBuffers: boolean;
Expand All @@ -607,6 +613,7 @@ export class BinMsg {
this.raw = message;
this.data = msgBody;
this.opts = opts ?? {
useBigInt64: false,
promoteLongs: true,
promoteValues: true,
promoteBuffers: false,
Expand All @@ -625,6 +632,7 @@ export class BinMsg {
this.checksumPresent = (this.responseFlags & OPTS_CHECKSUM_PRESENT) !== 0;
this.moreToCome = (this.responseFlags & OPTS_MORE_TO_COME) !== 0;
this.exhaustAllowed = (this.responseFlags & OPTS_EXHAUST_ALLOWED) !== 0;
this.useBigInt64 = typeof this.opts.useBigInt64 === 'boolean' ? this.opts.useBigInt64 : false;
this.promoteLongs = typeof this.opts.promoteLongs === 'boolean' ? this.opts.promoteLongs : true;
this.promoteValues =
typeof this.opts.promoteValues === 'boolean' ? this.opts.promoteValues : true;
Expand All @@ -648,6 +656,7 @@ export class BinMsg {
// Allow the return of raw documents instead of parsing
const raw = options.raw || false;
const documentsReturnedIn = options.documentsReturnedIn || null;
const useBigInt64 = options.useBigInt64 ?? this.opts.useBigInt64;
const promoteLongs = options.promoteLongs ?? this.opts.promoteLongs;
const promoteValues = options.promoteValues ?? this.opts.promoteValues;
const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers;
Expand All @@ -656,6 +665,7 @@ export class BinMsg {

// Set up the options
const bsonOptions: BSONSerializeOptions = {
useBigInt64,
promoteLongs,
promoteValues,
promoteBuffers,
Expand Down
1 change: 1 addition & 0 deletions src/cmap/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ function write(
command: !!options.command,

// for BSON parsing
useBigInt64: typeof options.useBigInt64 === 'boolean' ? options.useBigInt64 : false,
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false,
Expand Down
12 changes: 12 additions & 0 deletions src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,15 @@ export function parseOptions(
mongoClient = undefined;
}

// validate BSONOptions
if (options.useBigInt64 && !(options.promoteLongs ?? true)) {
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
throw new MongoAPIError('Must request either bigint or Long for int64 deserialization');
}

if (options.useBigInt64 && !(options.promoteValues ?? true)) {
throw new MongoAPIError('Must request either bigint or Long for int64 deserialization');
}

const url = new ConnectionString(uri);
const { hosts, isSRV } = url;

Expand Down Expand Up @@ -955,6 +964,9 @@ export const OPTIONS = {
promoteValues: {
type: 'boolean'
},
useBigInt64: {
nbbeeken marked this conversation as resolved.
Show resolved Hide resolved
type: 'boolean'
},
proxyHost: {
type: 'string'
},
Expand Down
4 changes: 4 additions & 0 deletions src/cursor/abstract_cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,8 @@ export abstract class AbstractCursor<
this[kId] =
typeof response.cursor.id === 'number'
? Long.fromNumber(response.cursor.id)
baileympearson marked this conversation as resolved.
Show resolved Hide resolved
: typeof response.cursor.id === 'bigint'
? Long.fromBigInt(response.cursor.id)
: response.cursor.id;

if (response.cursor.ns) {
Expand Down Expand Up @@ -741,6 +743,8 @@ export function next<T>(
const cursorId =
typeof response.cursor.id === 'number'
? Long.fromNumber(response.cursor.id)
: typeof response.cursor.id === 'bigint'
? Long.fromBigInt(response.cursor.id)
: response.cursor.id;

cursor[kDocuments].pushMany(response.cursor.nextBatch);
Expand Down
1 change: 1 addition & 0 deletions src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const DB_OPTIONS_ALLOW_LIST = [
'readConcern',
'retryMiliSeconds',
'numberOfRetries',
'useBigInt64',
'promoteBuffers',
'promoteLongs',
'bsonRegExp',
Expand Down
3 changes: 2 additions & 1 deletion src/mongo_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export type ArrayElement<Type> = Type extends ReadonlyArray<infer Item> ? Item :
export type SchemaMember<T, V> = { [P in keyof T]?: V } | { [key: string]: V };

/** @public */
export type IntegerType = number | Int32 | Long;
export type IntegerType = number | Int32 | Long | bigint;

/** @public */
export type NumericType = IntegerType | Decimal128 | Double;
Expand Down Expand Up @@ -454,6 +454,7 @@ export type NestedPaths<Type, Depth extends number[]> = Depth['length'] extends
: Type extends
| string
| number
| bigint
| boolean
| Date
| RegExp
Expand Down
1 change: 1 addition & 0 deletions src/operations/create_collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const ILLEGAL_COMMAND_FIELDS = new Set([
'writeConcern',
'raw',
'fieldsAsRaw',
'useBigInt64',
'promoteLongs',
'promoteValues',
'promoteBuffers',
Expand Down
1 change: 1 addition & 0 deletions src/sdam/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
// force BSON serialization options
{
raw: false,
useBigInt64: false,
promoteLongs: true,
promoteValues: true,
promoteBuffers: true
Expand Down
3 changes: 2 additions & 1 deletion src/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
if (
!clusterTime.signature ||
clusterTime.signature.hash?._bsontype !== 'Binary' ||
(typeof clusterTime.signature.keyId !== 'number' &&
(typeof clusterTime.signature.keyId !== 'bigint' &&
typeof clusterTime.signature.keyId !== 'number' &&
clusterTime.signature.keyId?._bsontype !== 'Long') // apparently we decode the key to number?
) {
throw new MongoInvalidArgumentError(
Expand Down
64 changes: 60 additions & 4 deletions test/integration/change-streams/change_stream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,14 @@ describe('Change Streams', function () {
it('should close the listeners after the cursor is closed', {
metadata: { requires: { topology: 'replicaset' } },
async test() {
const collection = db.collection('closesListeners');
const changeStream = collection.watch(pipeline);
const willBeChanges = on(changeStream, 'change');
await once(changeStream.cursor, 'init');
await collection.insertOne({ a: 1 });

await willBeChanges.next();
expect(changeStream.cursorStream.listenerCount('data')).to.equal(1);
expect(changeStream.cursorStream?.listenerCount('data')).to.equal(1);

await changeStream.close();
expect(changeStream.cursorStream).to.not.exist;
Expand Down Expand Up @@ -1670,7 +1672,7 @@ describe('Change Streams', function () {
it('does not convert Longs to numbers', {
metadata: { requires: { topology: '!single' } },
test: async function () {
cs = collection.watch([], { promoteLongs: true });
cs = collection.watch([], { promoteLongs: true, useBigInt64: false });
durran marked this conversation as resolved.
Show resolved Hide resolved

const willBeChange = once(cs, 'change').then(args => args[0]);
await once(cs.cursor, 'init');
Expand All @@ -1689,7 +1691,7 @@ describe('Change Streams', function () {
it('converts Long values to native numbers', {
metadata: { requires: { topology: '!single' } },
test: async function () {
cs = collection.watch([], { promoteLongs: false });
cs = collection.watch([], { promoteLongs: false, useBigInt64: false });

const willBeChange = once(cs, 'change').then(args => args[0]);
await once(cs.cursor, 'init');
Expand All @@ -1707,7 +1709,7 @@ describe('Change Streams', function () {
it('defaults to true', {
metadata: { requires: { topology: '!single' } },
test: async function () {
cs = collection.watch([]);
cs = collection.watch([], { useBigInt64: false });

const willBeChange = once(cs, 'change').then(args => args[0]);
await once(cs.cursor, 'init');
Expand All @@ -1722,6 +1724,60 @@ describe('Change Streams', function () {
});
});

context('useBigInt64', () => {
const useBigInt64FalseTest = async function (options: ChangeStreamOptions) {
W-A-James marked this conversation as resolved.
Show resolved Hide resolved
cs = collection.watch([], options);
const willBeChange = once(cs, 'change').then(args => args[0]);
await once(cs.cursor, 'init');

await collection.insertOne({ a: Long.fromNumber(10) });

const change = await willBeChange;

expect(typeof change.fullDocument.a).to.equal('number');
};

context('when set to false', function () {
it('converts Long to number', {
metadata: {
requires: { topology: '!single' }
},
test: async function () {
await useBigInt64FalseTest({ useBigInt64: false });
}
});
});

context('when set to true', function () {
it('converts Long to bigint', {
metadata: {
requires: { topology: '!single' }
},
test: async function () {
cs = collection.watch([], { useBigInt64: true });
const willBeChange = once(cs, 'change').then(args => args[0]);
await once(cs.cursor, 'init');

await collection.insertOne({ a: Long.fromNumber(10) });

const change = await willBeChange;

expect(change.fullDocument).property('a').to.be.a('bigint');
expect(change.fullDocument).property('a', 10n);
}
});
});

context('when unset', function () {
it('defaults to false', {
metadata: { requires: { topology: '!single' } },
test: async function () {
await useBigInt64FalseTest({});
}
});
});
});

context('invalid options', function () {
it('does not send invalid options on the aggregate command', {
metadata: { requires: { topology: '!single' } },
Expand Down
Loading