mongodb 3.6.4 → 3.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/HISTORY.md +43 -0
  2. package/README.md +63 -67
  3. package/lib/bulk/common.js +76 -0
  4. package/lib/cmap/connection.js +36 -32
  5. package/lib/collection.js +50 -34
  6. package/lib/core/auth/mongo_credentials.js +5 -5
  7. package/lib/core/auth/scram.js +2 -1
  8. package/lib/core/connection/connect.js +1 -1
  9. package/lib/core/connection/logger.js +1 -0
  10. package/lib/core/connection/msg.js +3 -1
  11. package/lib/core/connection/utils.js +9 -14
  12. package/lib/core/error.js +2 -2
  13. package/lib/core/index.js +1 -1
  14. package/lib/core/sdam/monitor.js +8 -3
  15. package/lib/core/sdam/topology.js +2 -2
  16. package/lib/core/sdam/topology_description.js +24 -6
  17. package/lib/core/tools/smoke_plugin.js +1 -0
  18. package/lib/core/topologies/read_preference.js +2 -1
  19. package/lib/core/topologies/replset.js +1 -1
  20. package/lib/core/uri_parser.js +2 -1
  21. package/lib/core/utils.js +2 -5
  22. package/lib/core/wireprotocol/command.js +10 -5
  23. package/lib/core/wireprotocol/kill_cursors.js +2 -1
  24. package/lib/db.js +9 -3
  25. package/lib/encrypter.js +163 -0
  26. package/lib/gridfs-stream/upload.js +1 -0
  27. package/lib/mongo_client.js +123 -172
  28. package/lib/operations/add_user.js +2 -1
  29. package/lib/operations/bulk_write.js +0 -8
  30. package/lib/operations/connect.js +14 -61
  31. package/lib/operations/execute_operation.js +47 -69
  32. package/lib/operations/find.js +3 -0
  33. package/lib/operations/find_one_and_replace.js +8 -2
  34. package/lib/operations/find_one_and_update.js +8 -3
  35. package/lib/operations/insert_many.js +1 -5
  36. package/lib/operations/operation.js +1 -1
  37. package/lib/utils.js +88 -30
  38. package/lib/write_concern.js +5 -1
  39. package/package.json +22 -22
package/lib/collection.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const deprecate = require('util').deprecate;
4
4
  const deprecateOptions = require('./utils').deprecateOptions;
5
+ const emitWarningOnce = require('./utils').emitWarningOnce;
5
6
  const checkCollectionName = require('./utils').checkCollectionName;
6
7
  const ObjectID = require('./core').BSON.ObjectID;
7
8
  const MongoError = require('./core').MongoError;
@@ -323,7 +324,7 @@ Collection.prototype.find = deprecateOptions(
323
324
  function(query, options, callback) {
324
325
  if (typeof callback === 'object') {
325
326
  // TODO(MAJOR): throw in the future
326
- console.warn('Third parameter to `find()` must be a callback or undefined');
327
+ emitWarningOnce('Third parameter to `find()` must be a callback or undefined');
327
328
  }
328
329
 
329
330
  let selector = query;
@@ -1092,7 +1093,7 @@ Collection.prototype.findOne = deprecateOptions(
1092
1093
  function(query, options, callback) {
1093
1094
  if (typeof callback === 'object') {
1094
1095
  // TODO(MAJOR): throw in the future
1095
- console.warn('Third parameter to `findOne()` must be a callback or undefined');
1096
+ emitWarningOnce('Third parameter to `findOne()` must be a callback or undefined');
1096
1097
  }
1097
1098
 
1098
1099
  if (typeof query === 'function') (callback = query), (query = {}), (options = {});
@@ -1654,7 +1655,7 @@ Collection.prototype.stats = function(options, callback) {
1654
1655
 
1655
1656
  /**
1656
1657
  * @typedef {Object} Collection~findAndModifyWriteOpResult
1657
- * @property {object} value Document returned from the `findAndModify` command. If no documents were found, `value` will be `null` by default (`returnOriginal: true`), even if a document was upserted; if `returnOriginal` was false, the upserted document will be returned in that case.
1658
+ * @property {object} value Document returned from the `findAndModify` command. If no documents were found, `value` will be `null` by default even if a document was upserted unless `returnDocument` is specified as `'after'`, in which case the upserted document will be returned.
1658
1659
  * @property {object} lastErrorObject The raw lastErrorObject returned from the command. See {@link https://docs.mongodb.com/manual/reference/command/findAndModify/index.html#lasterrorobject|findAndModify command documentation}.
1659
1660
  * @property {Number} ok Is 1 if the command executed correctly.
1660
1661
  */
@@ -1715,7 +1716,8 @@ Collection.prototype.findOneAndDelete = function(filter, options, callback) {
1715
1716
  * @param {object} [options.projection] Limits the fields to return for all matching documents.
1716
1717
  * @param {object} [options.sort] Determines which document the operation modifies if the query selects multiple documents.
1717
1718
  * @param {boolean} [options.upsert=false] Upsert the document if it does not exist.
1718
- * @param {boolean} [options.returnOriginal=true] When false, returns the updated document rather than the original. The default is true.
1719
+ * @param {'before'|'after'} [options.returnDocument='before'] When set to `'after'`, returns the updated document rather than the original. The default is `'before'`.
1720
+ * @param {boolean} [options.returnOriginal=true] **Deprecated** Use `options.returnDocument` instead.
1719
1721
  * @param {boolean} [options.checkKeys=false] If true, will throw if bson documents start with `$` or include a `.` in any key value
1720
1722
  * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
1721
1723
  * @param {boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
@@ -1724,22 +1726,29 @@ Collection.prototype.findOneAndDelete = function(filter, options, callback) {
1724
1726
  * @param {Collection~findAndModifyCallback} [callback] The collection result callback
1725
1727
  * @return {Promise<Collection~findAndModifyWriteOpResultObject>} returns Promise if no callback passed
1726
1728
  */
1727
- Collection.prototype.findOneAndReplace = function(filter, replacement, options, callback) {
1728
- if (typeof options === 'function') (callback = options), (options = {});
1729
- options = options || {};
1729
+ Collection.prototype.findOneAndReplace = deprecateOptions(
1730
+ {
1731
+ name: 'collection.findOneAndReplace',
1732
+ deprecatedOptions: ['returnOriginal'],
1733
+ optionsIndex: 2
1734
+ },
1735
+ function(filter, replacement, options, callback) {
1736
+ if (typeof options === 'function') (callback = options), (options = {});
1737
+ options = options || {};
1730
1738
 
1731
- // Add ignoreUndefined
1732
- if (this.s.options.ignoreUndefined) {
1733
- options = Object.assign({}, options);
1734
- options.ignoreUndefined = this.s.options.ignoreUndefined;
1735
- }
1739
+ // Add ignoreUndefined
1740
+ if (this.s.options.ignoreUndefined) {
1741
+ options = Object.assign({}, options);
1742
+ options.ignoreUndefined = this.s.options.ignoreUndefined;
1743
+ }
1736
1744
 
1737
- return executeOperation(
1738
- this.s.topology,
1739
- new FindOneAndReplaceOperation(this, filter, replacement, options),
1740
- callback
1741
- );
1742
- };
1745
+ return executeOperation(
1746
+ this.s.topology,
1747
+ new FindOneAndReplaceOperation(this, filter, replacement, options),
1748
+ callback
1749
+ );
1750
+ }
1751
+ );
1743
1752
 
1744
1753
  /**
1745
1754
  * Find a document and update it in one atomic operation. Requires a write lock for the duration of the operation.
@@ -1756,7 +1765,8 @@ Collection.prototype.findOneAndReplace = function(filter, replacement, options,
1756
1765
  * @param {object} [options.projection] Limits the fields to return for all matching documents.
1757
1766
  * @param {object} [options.sort] Determines which document the operation modifies if the query selects multiple documents.
1758
1767
  * @param {boolean} [options.upsert=false] Upsert the document if it does not exist.
1759
- * @param {boolean} [options.returnOriginal=true] When false, returns the updated document rather than the original. The default is true.
1768
+ * @param {'before'|'after'} [options.returnDocument='before'] When set to `'after'`, returns the updated document rather than the original. The default is `'before'`.
1769
+ * @param {boolean} [options.returnOriginal=true] **Deprecated** Use `options.returnDocument` instead.
1760
1770
  * @param {boolean} [options.checkKeys=false] If true, will throw if bson documents start with `$` or include a `.` in any key value
1761
1771
  * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
1762
1772
  * @param {boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
@@ -1765,22 +1775,29 @@ Collection.prototype.findOneAndReplace = function(filter, replacement, options,
1765
1775
  * @param {Collection~findAndModifyCallback} [callback] The collection result callback
1766
1776
  * @return {Promise<Collection~findAndModifyWriteOpResultObject>} returns Promise if no callback passed
1767
1777
  */
1768
- Collection.prototype.findOneAndUpdate = function(filter, update, options, callback) {
1769
- if (typeof options === 'function') (callback = options), (options = {});
1770
- options = options || {};
1778
+ Collection.prototype.findOneAndUpdate = deprecateOptions(
1779
+ {
1780
+ name: 'collection.findOneAndUpdate',
1781
+ deprecatedOptions: ['returnOriginal'],
1782
+ optionsIndex: 2
1783
+ },
1784
+ function(filter, update, options, callback) {
1785
+ if (typeof options === 'function') (callback = options), (options = {});
1786
+ options = options || {};
1771
1787
 
1772
- // Add ignoreUndefined
1773
- if (this.s.options.ignoreUndefined) {
1774
- options = Object.assign({}, options);
1775
- options.ignoreUndefined = this.s.options.ignoreUndefined;
1776
- }
1788
+ // Add ignoreUndefined
1789
+ if (this.s.options.ignoreUndefined) {
1790
+ options = Object.assign({}, options);
1791
+ options.ignoreUndefined = this.s.options.ignoreUndefined;
1792
+ }
1777
1793
 
1778
- return executeOperation(
1779
- this.s.topology,
1780
- new FindOneAndUpdateOperation(this, filter, update, options),
1781
- callback
1782
- );
1783
- };
1794
+ return executeOperation(
1795
+ this.s.topology,
1796
+ new FindOneAndUpdateOperation(this, filter, update, options),
1797
+ callback
1798
+ );
1799
+ }
1800
+ );
1784
1801
 
1785
1802
  /**
1786
1803
  * Find and update a document.
@@ -2198,7 +2215,6 @@ Collection.prototype.initializeUnorderedBulkOp = function(options) {
2198
2215
  * @param {object|WriteConcern} [options.writeConcern] Specify write concern settings.
2199
2216
  * @param {ClientSession} [options.session] optional session to use for this operation
2200
2217
  * @param {boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
2201
- * @param {OrderedBulkOperation} callback The command result callback
2202
2218
  * @return {null}
2203
2219
  */
2204
2220
  Collection.prototype.initializeOrderedBulkOp = function(options) {
@@ -49,16 +49,16 @@ class MongoCredentials {
49
49
  this.mechanism = options.mechanism || 'default';
50
50
  this.mechanismProperties = options.mechanismProperties || {};
51
51
 
52
- if (this.mechanism.match(/MONGODB-AWS/i)) {
53
- if (this.username == null && process.env.AWS_ACCESS_KEY_ID) {
52
+ if (/MONGODB-AWS/i.test(this.mechanism)) {
53
+ if (!this.username && process.env.AWS_ACCESS_KEY_ID) {
54
54
  this.username = process.env.AWS_ACCESS_KEY_ID;
55
55
  }
56
56
 
57
- if (this.password == null && process.env.AWS_SECRET_ACCESS_KEY) {
57
+ if (!this.password && process.env.AWS_SECRET_ACCESS_KEY) {
58
58
  this.password = process.env.AWS_SECRET_ACCESS_KEY;
59
59
  }
60
60
 
61
- if (this.mechanismProperties.AWS_SESSION_TOKEN == null && process.env.AWS_SESSION_TOKEN) {
61
+ if (!this.mechanismProperties.AWS_SESSION_TOKEN && process.env.AWS_SESSION_TOKEN) {
62
62
  this.mechanismProperties.AWS_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN;
63
63
  }
64
64
  }
@@ -90,7 +90,7 @@ class MongoCredentials {
90
90
  */
91
91
  resolveAuthMechanism(ismaster) {
92
92
  // If the mechanism is not "default", then it does not need to be resolved
93
- if (this.mechanism.match(/DEFAULT/i)) {
93
+ if (/DEFAULT/i.test(this.mechanism)) {
94
94
  return new MongoCredentials({
95
95
  username: this.username,
96
96
  password: this.password,
@@ -4,6 +4,7 @@ const Buffer = require('safe-buffer').Buffer;
4
4
  const retrieveBSON = require('../connection/utils').retrieveBSON;
5
5
  const MongoError = require('../error').MongoError;
6
6
  const AuthProvider = require('./auth_provider').AuthProvider;
7
+ const emitWarningOnce = require('../../utils').emitWarning;
7
8
 
8
9
  const BSON = retrieveBSON();
9
10
  const Binary = BSON.Binary;
@@ -24,7 +25,7 @@ class ScramSHA extends AuthProvider {
24
25
  prepare(handshakeDoc, authContext, callback) {
25
26
  const cryptoMethod = this.cryptoMethod;
26
27
  if (cryptoMethod === 'sha256' && saslprep == null) {
27
- console.warn('Warning: no saslprep library specified. Passwords will not be sanitized');
28
+ emitWarningOnce('Warning: no saslprep library specified. Passwords will not be sanitized');
28
29
  }
29
30
 
30
31
  crypto.randomBytes(24, (err, nonce) => {
@@ -243,7 +243,7 @@ function parseSslOptions(family, options) {
243
243
  }
244
244
 
245
245
  // Set default sni servername to be the same as host
246
- if (result.servername == null) {
246
+ if (result.servername == null && !net.isIP(result.host)) {
247
247
  result.servername = result.host;
248
248
  }
249
249
 
@@ -37,6 +37,7 @@ var Logger = function(className, options) {
37
37
  if (options.logger) {
38
38
  currentLogger = options.logger;
39
39
  } else if (currentLogger == null) {
40
+ // eslint-disable-next-line no-console
40
41
  currentLogger = console.log;
41
42
  }
42
43
 
@@ -31,6 +31,7 @@ const Buffer = require('safe-buffer').Buffer;
31
31
  const opcodes = require('../wireprotocol/shared').opcodes;
32
32
  const databaseNamespace = require('../wireprotocol/shared').databaseNamespace;
33
33
  const ReadPreference = require('../topologies/read_preference');
34
+ const MongoError = require('../../core/error').MongoError;
34
35
 
35
36
  // Incrementing request id
36
37
  let _requestId = 0;
@@ -196,7 +197,8 @@ class BinMsg {
196
197
  while (this.index < this.data.length) {
197
198
  const payloadType = this.data.readUInt8(this.index++);
198
199
  if (payloadType === 1) {
199
- console.error('TYPE 1');
200
+ // It was decided that no driver makes use of payload type 1
201
+ throw new MongoError('OP_MSG Payload Type 1 detected unsupported protocol');
200
202
  } else if (payloadType === 0) {
201
203
  const bsonSize = this.data.readUInt32LE(this.index);
202
204
  const bin = this.data.slice(this.index, this.index + bsonSize);
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const require_optional = require('require_optional');
3
+ const require_optional = require('optional-require')(require);
4
4
 
5
5
  function debugOptions(debugFields, options) {
6
- var finaloptions = {};
6
+ const finaloptions = {};
7
7
  debugFields.forEach(function(n) {
8
8
  finaloptions[n] = options[n];
9
9
  });
@@ -12,16 +12,14 @@ function debugOptions(debugFields, options) {
12
12
  }
13
13
 
14
14
  function retrieveBSON() {
15
- var BSON = require('bson');
15
+ const BSON = require('bson');
16
16
  BSON.native = false;
17
17
 
18
- try {
19
- var optionalBSON = require_optional('bson-ext');
20
- if (optionalBSON) {
21
- optionalBSON.native = true;
22
- return optionalBSON;
23
- }
24
- } catch (err) {} // eslint-disable-line
18
+ const optionalBSON = require_optional('bson-ext');
19
+ if (optionalBSON) {
20
+ optionalBSON.native = true;
21
+ return optionalBSON;
22
+ }
25
23
 
26
24
  return BSON;
27
25
  }
@@ -35,10 +33,7 @@ function noSnappyWarning() {
35
33
 
36
34
  // Facilitate loading Snappy optionally
37
35
  function retrieveSnappy() {
38
- var snappy = null;
39
- try {
40
- snappy = require_optional('snappy');
41
- } catch (error) {} // eslint-disable-line
36
+ let snappy = require_optional('snappy');
42
37
  if (!snappy) {
43
38
  snappy = {
44
39
  compress: noSnappyWarning,
package/lib/core/error.js CHANGED
@@ -102,8 +102,8 @@ class MongoNetworkError extends MongoError {
102
102
  super(message);
103
103
  this.name = 'MongoNetworkError';
104
104
 
105
- if (options && options.beforeHandshake === true) {
106
- this[kBeforeHandshake] = true;
105
+ if (options && typeof options.beforeHandshake === 'boolean') {
106
+ this[kBeforeHandshake] = options.beforeHandshake;
107
107
  }
108
108
  }
109
109
  }
package/lib/core/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  let BSON = require('bson');
4
- const require_optional = require('require_optional');
4
+ const require_optional = require('optional-require')(require);
5
5
  const EJSON = require('./utils').retrieveEJSON();
6
6
 
7
7
  try {
@@ -91,6 +91,10 @@ class Monitor extends EventEmitter {
91
91
 
92
92
  // ensure no authentication is used for monitoring
93
93
  delete connectOptions.credentials;
94
+
95
+ // ensure encryption is not requested for monitoring
96
+ delete connectOptions.autoEncrypter;
97
+
94
98
  this.connectOptions = Object.freeze(connectOptions);
95
99
  }
96
100
 
@@ -222,9 +226,10 @@ function checkServer(monitor, callback) {
222
226
  }
223
227
 
224
228
  const isMaster = result.result;
225
- const duration = isAwaitable
226
- ? monitor[kRTTPinger].roundTripTime
227
- : calculateDurationInMs(start);
229
+ const rttPinger = monitor[kRTTPinger];
230
+
231
+ const duration =
232
+ isAwaitable && rttPinger ? rttPinger.roundTripTime : calculateDurationInMs(start);
228
233
 
229
234
  monitor.emit(
230
235
  'serverHeartbeatSucceeded',
@@ -27,6 +27,7 @@ const ServerSessionPool = require('../sessions').ServerSessionPool;
27
27
  const makeClientMetadata = require('../utils').makeClientMetadata;
28
28
  const CMAP_EVENT_NAMES = require('../../cmap/events').CMAP_EVENT_NAMES;
29
29
  const compareTopologyVersion = require('./server_description').compareTopologyVersion;
30
+ const emitWarning = require('../../utils').emitWarning;
30
31
 
31
32
  const common = require('./common');
32
33
  const drainTimerQueue = common.drainTimerQueue;
@@ -358,7 +359,6 @@ class Topology extends EventEmitter {
358
359
  this.emit('topologyClosed', new events.TopologyClosedEvent(this.s.id));
359
360
 
360
361
  stateTransition(this, STATE_CLOSED);
361
- this.emit('close');
362
362
 
363
363
  if (typeof callback === 'function') {
364
364
  callback(err);
@@ -739,7 +739,7 @@ class Topology extends EventEmitter {
739
739
  }
740
740
 
741
741
  unref() {
742
- console.log('not implemented: `unref`');
742
+ emitWarning('not implemented: `unref`');
743
743
  }
744
744
 
745
745
  // NOTE: There are many places in code where we explicitly check the last isMaster
@@ -72,12 +72,30 @@ class TopologyDescription {
72
72
  // value among ServerDescriptions of all data-bearing server types. If any have a null
73
73
  // logicalSessionTimeoutMinutes, then TopologyDescription.logicalSessionTimeoutMinutes MUST be
74
74
  // set to null.
75
- const readableServers = Array.from(this.servers.values()).filter(s => s.isReadable);
76
- this.logicalSessionTimeoutMinutes = readableServers.reduce((result, server) => {
77
- if (server.logicalSessionTimeoutMinutes == null) return null;
78
- if (result == null) return server.logicalSessionTimeoutMinutes;
79
- return Math.min(result, server.logicalSessionTimeoutMinutes);
80
- }, null);
75
+ this.logicalSessionTimeoutMinutes = null;
76
+ for (const addressServerTuple of this.servers) {
77
+ const server = addressServerTuple[1];
78
+ if (server.isReadable) {
79
+ if (server.logicalSessionTimeoutMinutes == null) {
80
+ // If any of the servers have a null logicalSessionsTimeout, then the whole topology does
81
+ this.logicalSessionTimeoutMinutes = null;
82
+ break;
83
+ }
84
+
85
+ if (this.logicalSessionTimeoutMinutes == null) {
86
+ // First server with a non null logicalSessionsTimeout
87
+ this.logicalSessionTimeoutMinutes = server.logicalSessionTimeoutMinutes;
88
+ continue;
89
+ }
90
+
91
+ // Always select the smaller of the:
92
+ // current server logicalSessionsTimeout and the topologies logicalSessionsTimeout
93
+ this.logicalSessionTimeoutMinutes = Math.min(
94
+ this.logicalSessionTimeoutMinutes,
95
+ server.logicalSessionTimeoutMinutes
96
+ );
97
+ }
98
+ }
81
99
  }
82
100
 
83
101
  /**
@@ -52,6 +52,7 @@ exports.attachToRunner = function(runner, outputFile) {
52
52
  fs.writeFileSync(outputFile, JSON.stringify(smokeOutput));
53
53
 
54
54
  // Standard NodeJS uncaught exception handler
55
+ // eslint-disable-next-line no-console
55
56
  console.error(err.stack);
56
57
  process.exit(1);
57
58
  });
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+ const emitWarningOnce = require('../../utils').emitWarningOnce;
2
3
 
3
4
  /**
4
5
  * The **ReadPreference** class is a class that represents a MongoDB ReadPreference and is
@@ -20,7 +21,7 @@ const ReadPreference = function(mode, tags, options) {
20
21
 
21
22
  // TODO(major): tags MUST be an array of tagsets
22
23
  if (tags && !Array.isArray(tags)) {
23
- console.warn(
24
+ emitWarningOnce(
24
25
  'ReadPreference tags must be an array, this will change in the next major version'
25
26
  );
26
27
 
@@ -558,7 +558,7 @@ var monitorServer = function(host, self, options) {
558
558
  self.s.options.secondaryOnlyConnectionAllowed) ||
559
559
  self.s.replicaSetState.hasPrimary())
560
560
  ) {
561
- stateTransition(self, CONNECTED);
561
+ stateTransition(self, CONNECTING);
562
562
 
563
563
  // Rexecute any stalled operation
564
564
  rexecuteOperations(self);
@@ -4,6 +4,7 @@ const qs = require('querystring');
4
4
  const dns = require('dns');
5
5
  const MongoParseError = require('./error').MongoParseError;
6
6
  const ReadPreference = require('./topologies/read_preference');
7
+ const emitWarningOnce = require('../utils').emitWarningOnce;
7
8
 
8
9
  /**
9
10
  * The following regular expression validates a connection string and breaks the
@@ -438,7 +439,7 @@ function parseQueryString(query, options) {
438
439
  // special cases for known deprecated options
439
440
  if (result.wtimeout && result.wtimeoutms) {
440
441
  delete result.wtimeout;
441
- console.warn('Unsupported option `wtimeout` specified');
442
+ emitWarningOnce('Unsupported option `wtimeout` specified');
442
443
  }
443
444
 
444
445
  return Object.keys(result).length ? result : null;
package/lib/core/utils.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
  const os = require('os');
3
3
  const crypto = require('crypto');
4
- const requireOptional = require('require_optional');
4
+ const requireOptional = require('optional-require')(require);
5
5
 
6
6
  /**
7
7
  * Generate a UUIDv4
@@ -46,10 +46,7 @@ const noEJSONError = function() {
46
46
 
47
47
  // Facilitate loading EJSON optionally
48
48
  function retrieveEJSON() {
49
- let EJSON = null;
50
- try {
51
- EJSON = requireOptional('mongodb-extjson');
52
- } catch (error) {} // eslint-disable-line
49
+ let EJSON = requireOptional('mongodb-extjson');
53
50
  if (!EJSON) {
54
51
  EJSON = {
55
52
  parse: noEJSONError,
@@ -45,14 +45,19 @@ function _command(server, ns, cmd, options, callback) {
45
45
  const shouldUseOpMsg = supportsOpMsg(server);
46
46
  const session = options.session;
47
47
 
48
- let clusterTime = server.clusterTime;
48
+ const serverClusterTime = server.clusterTime;
49
+ let clusterTime = serverClusterTime;
49
50
  let finalCmd = Object.assign({}, cmd);
50
51
  if (hasSessionSupport(server) && session) {
52
+ const sessionClusterTime = session.clusterTime;
51
53
  if (
52
- session.clusterTime &&
53
- session.clusterTime.clusterTime.greaterThan(clusterTime.clusterTime)
54
+ serverClusterTime &&
55
+ serverClusterTime.clusterTime &&
56
+ sessionClusterTime &&
57
+ sessionClusterTime.clusterTime &&
58
+ sessionClusterTime.clusterTime.greaterThan(serverClusterTime.clusterTime)
54
59
  ) {
55
- clusterTime = session.clusterTime;
60
+ clusterTime = sessionClusterTime;
56
61
  }
57
62
 
58
63
  const err = applySession(session, finalCmd, options);
@@ -61,8 +66,8 @@ function _command(server, ns, cmd, options, callback) {
61
66
  }
62
67
  }
63
68
 
64
- // if we have a known cluster time, gossip it
65
69
  if (clusterTime) {
70
+ // if we have a known cluster time, gossip it
66
71
  finalCmd.$clusterTime = clusterTime;
67
72
  }
68
73
 
@@ -5,6 +5,7 @@ const MongoError = require('../error').MongoError;
5
5
  const MongoNetworkError = require('../error').MongoNetworkError;
6
6
  const collectionNamespace = require('./shared').collectionNamespace;
7
7
  const maxWireVersion = require('../utils').maxWireVersion;
8
+ const emitWarning = require('../utils').emitWarning;
8
9
  const command = require('./command');
9
10
 
10
11
  function killCursors(server, ns, cursorState, callback) {
@@ -31,7 +32,7 @@ function killCursors(server, ns, cursorState, callback) {
31
32
  if (typeof callback === 'function') {
32
33
  callback(err, null);
33
34
  } else {
34
- console.warn(err);
35
+ emitWarning(err);
35
36
  }
36
37
  }
37
38
  }
package/lib/db.js CHANGED
@@ -12,7 +12,7 @@ const MongoError = require('./core').MongoError;
12
12
  const ObjectID = require('./core').ObjectID;
13
13
  const Logger = require('./core').Logger;
14
14
  const Collection = require('./collection');
15
- const mergeOptionsAndWriteConcern = require('./utils').mergeOptionsAndWriteConcern;
15
+ const conditionallyMergeWriteConcern = require('./utils').conditionallyMergeWriteConcern;
16
16
  const executeLegacyOperation = require('./utils').executeLegacyOperation;
17
17
  const ChangeStream = require('./change_stream');
18
18
  const deprecate = require('util').deprecate;
@@ -382,7 +382,7 @@ Db.prototype.admin = function() {
382
382
  * @param {AggregationCursor} cursor The cursor if the aggregation command was executed successfully.
383
383
  */
384
384
 
385
- const collectionKeys = [
385
+ const COLLECTION_OPTION_KEYS = [
386
386
  'pkFactory',
387
387
  'readPreference',
388
388
  'serializeFunctions',
@@ -433,8 +433,14 @@ Db.prototype.collection = function(name, options, callback) {
433
433
  options.ignoreUndefined = this.s.options.ignoreUndefined;
434
434
  }
435
435
 
436
+ for (const collectionOptionKey of COLLECTION_OPTION_KEYS) {
437
+ if (!(collectionOptionKey in options) && this.s.options[collectionOptionKey] !== undefined) {
438
+ options[collectionOptionKey] = this.s.options[collectionOptionKey];
439
+ }
440
+ }
441
+
436
442
  // Merge in all needed options and ensure correct writeConcern merging from db level
437
- options = mergeOptionsAndWriteConcern(options, this.s.options, collectionKeys, true);
443
+ options = conditionallyMergeWriteConcern(options, this.s.options);
438
444
 
439
445
  // Execute
440
446
  if (options == null || !options.strict) {