PageRenderTime 38ms CodeModel.GetById 9ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 1ms

/node_modules/mongoose/node_modules/mongodb-core/lib/connection/commands.js

https://bitbucket.org/coleman333/smartsite
JavaScript | 531 lines | 343 code | 84 blank | 104 comment | 37 complexity | a1e7043fbdc21b598a43b0c8f23799e5 MD5 | raw file
  1'use strict';
  2
  3var retrieveBSON = require('./utils').retrieveBSON;
  4var BSON = retrieveBSON();
  5var Long = BSON.Long;
  6
  7// Incrementing request id
  8var _requestId = 0;
  9
 10// Wire command operation ids
 11var opcodes = require('../wireprotocol/shared').opcodes;
 12
 13// Query flags
 14var OPTS_TAILABLE_CURSOR = 2;
 15var OPTS_SLAVE = 4;
 16var OPTS_OPLOG_REPLAY = 8;
 17var OPTS_NO_CURSOR_TIMEOUT = 16;
 18var OPTS_AWAIT_DATA = 32;
 19var OPTS_EXHAUST = 64;
 20var OPTS_PARTIAL = 128;
 21
 22// Response flags
 23var CURSOR_NOT_FOUND = 0;
 24var QUERY_FAILURE = 2;
 25var SHARD_CONFIG_STALE = 4;
 26var AWAIT_CAPABLE = 8;
 27
 28/**************************************************************
 29 * QUERY
 30 **************************************************************/
 31var Query = function(bson, ns, query, options) {
 32  var self = this;
 33  // Basic options needed to be passed in
 34  if (ns == null) throw new Error('ns must be specified for query');
 35  if (query == null) throw new Error('query must be specified for query');
 36
 37  // Validate that we are not passing 0x00 in the collection name
 38  if (ns.indexOf('\x00') !== -1) {
 39    throw new Error('namespace cannot contain a null character');
 40  }
 41
 42  // Basic options
 43  this.bson = bson;
 44  this.ns = ns;
 45  this.query = query;
 46
 47  // Ensure empty options
 48  this.options = options || {};
 49
 50  // Additional options
 51  this.numberToSkip = options.numberToSkip || 0;
 52  this.numberToReturn = options.numberToReturn || 0;
 53  this.returnFieldSelector = options.returnFieldSelector || null;
 54  this.requestId = Query.getRequestId();
 55
 56  // Serialization option
 57  this.serializeFunctions =
 58    typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
 59  this.ignoreUndefined =
 60    typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
 61  this.maxBsonSize = options.maxBsonSize || 1024 * 1024 * 16;
 62  this.checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
 63  this.batchSize = self.numberToReturn;
 64
 65  // Flags
 66  this.tailable = false;
 67  this.slaveOk = typeof options.slaveOk === 'boolean' ? options.slaveOk : false;
 68  this.oplogReplay = false;
 69  this.noCursorTimeout = false;
 70  this.awaitData = false;
 71  this.exhaust = false;
 72  this.partial = false;
 73};
 74
 75//
 76// Assign a new request Id
 77Query.prototype.incRequestId = function() {
 78  this.requestId = _requestId++;
 79};
 80
 81//
 82// Assign a new request Id
 83Query.nextRequestId = function() {
 84  return _requestId + 1;
 85};
 86
 87//
 88// Uses a single allocated buffer for the process, avoiding multiple memory allocations
 89Query.prototype.toBin = function() {
 90  var self = this;
 91  var buffers = [];
 92  var projection = null;
 93
 94  // Set up the flags
 95  var flags = 0;
 96  if (this.tailable) {
 97    flags |= OPTS_TAILABLE_CURSOR;
 98  }
 99
100  if (this.slaveOk) {
101    flags |= OPTS_SLAVE;
102  }
103
104  if (this.oplogReplay) {
105    flags |= OPTS_OPLOG_REPLAY;
106  }
107
108  if (this.noCursorTimeout) {
109    flags |= OPTS_NO_CURSOR_TIMEOUT;
110  }
111
112  if (this.awaitData) {
113    flags |= OPTS_AWAIT_DATA;
114  }
115
116  if (this.exhaust) {
117    flags |= OPTS_EXHAUST;
118  }
119
120  if (this.partial) {
121    flags |= OPTS_PARTIAL;
122  }
123
124  // If batchSize is different to self.numberToReturn
125  if (self.batchSize !== self.numberToReturn) self.numberToReturn = self.batchSize;
126
127  // Allocate write protocol header buffer
128  var header = new Buffer(
129    4 * 4 + // Header
130    4 + // Flags
131    Buffer.byteLength(self.ns) +
132    1 + // namespace
133    4 + // numberToSkip
134      4 // numberToReturn
135  );
136
137  // Add header to buffers
138  buffers.push(header);
139
140  // Serialize the query
141  var query = self.bson.serialize(this.query, {
142    checkKeys: this.checkKeys,
143    serializeFunctions: this.serializeFunctions,
144    ignoreUndefined: this.ignoreUndefined
145  });
146
147  // Add query document
148  buffers.push(query);
149
150  if (self.returnFieldSelector && Object.keys(self.returnFieldSelector).length > 0) {
151    // Serialize the projection document
152    projection = self.bson.serialize(this.returnFieldSelector, {
153      checkKeys: this.checkKeys,
154      serializeFunctions: this.serializeFunctions,
155      ignoreUndefined: this.ignoreUndefined
156    });
157    // Add projection document
158    buffers.push(projection);
159  }
160
161  // Total message size
162  var totalLength = header.length + query.length + (projection ? projection.length : 0);
163
164  // Set up the index
165  var index = 4;
166
167  // Write total document length
168  header[3] = (totalLength >> 24) & 0xff;
169  header[2] = (totalLength >> 16) & 0xff;
170  header[1] = (totalLength >> 8) & 0xff;
171  header[0] = totalLength & 0xff;
172
173  // Write header information requestId
174  header[index + 3] = (this.requestId >> 24) & 0xff;
175  header[index + 2] = (this.requestId >> 16) & 0xff;
176  header[index + 1] = (this.requestId >> 8) & 0xff;
177  header[index] = this.requestId & 0xff;
178  index = index + 4;
179
180  // Write header information responseTo
181  header[index + 3] = (0 >> 24) & 0xff;
182  header[index + 2] = (0 >> 16) & 0xff;
183  header[index + 1] = (0 >> 8) & 0xff;
184  header[index] = 0 & 0xff;
185  index = index + 4;
186
187  // Write header information OP_QUERY
188  header[index + 3] = (opcodes.OP_QUERY >> 24) & 0xff;
189  header[index + 2] = (opcodes.OP_QUERY >> 16) & 0xff;
190  header[index + 1] = (opcodes.OP_QUERY >> 8) & 0xff;
191  header[index] = opcodes.OP_QUERY & 0xff;
192  index = index + 4;
193
194  // Write header information flags
195  header[index + 3] = (flags >> 24) & 0xff;
196  header[index + 2] = (flags >> 16) & 0xff;
197  header[index + 1] = (flags >> 8) & 0xff;
198  header[index] = flags & 0xff;
199  index = index + 4;
200
201  // Write collection name
202  index = index + header.write(this.ns, index, 'utf8') + 1;
203  header[index - 1] = 0;
204
205  // Write header information flags numberToSkip
206  header[index + 3] = (this.numberToSkip >> 24) & 0xff;
207  header[index + 2] = (this.numberToSkip >> 16) & 0xff;
208  header[index + 1] = (this.numberToSkip >> 8) & 0xff;
209  header[index] = this.numberToSkip & 0xff;
210  index = index + 4;
211
212  // Write header information flags numberToReturn
213  header[index + 3] = (this.numberToReturn >> 24) & 0xff;
214  header[index + 2] = (this.numberToReturn >> 16) & 0xff;
215  header[index + 1] = (this.numberToReturn >> 8) & 0xff;
216  header[index] = this.numberToReturn & 0xff;
217  index = index + 4;
218
219  // Return the buffers
220  return buffers;
221};
222
223Query.getRequestId = function() {
224  return ++_requestId;
225};
226
227/**************************************************************
228 * GETMORE
229 **************************************************************/
230var GetMore = function(bson, ns, cursorId, opts) {
231  opts = opts || {};
232  this.numberToReturn = opts.numberToReturn || 0;
233  this.requestId = _requestId++;
234  this.bson = bson;
235  this.ns = ns;
236  this.cursorId = cursorId;
237};
238
239//
240// Uses a single allocated buffer for the process, avoiding multiple memory allocations
241GetMore.prototype.toBin = function() {
242  var length = 4 + Buffer.byteLength(this.ns) + 1 + 4 + 8 + 4 * 4;
243  // Create command buffer
244  var index = 0;
245  // Allocate buffer
246  var _buffer = new Buffer(length);
247
248  // Write header information
249  // index = write32bit(index, _buffer, length);
250  _buffer[index + 3] = (length >> 24) & 0xff;
251  _buffer[index + 2] = (length >> 16) & 0xff;
252  _buffer[index + 1] = (length >> 8) & 0xff;
253  _buffer[index] = length & 0xff;
254  index = index + 4;
255
256  // index = write32bit(index, _buffer, requestId);
257  _buffer[index + 3] = (this.requestId >> 24) & 0xff;
258  _buffer[index + 2] = (this.requestId >> 16) & 0xff;
259  _buffer[index + 1] = (this.requestId >> 8) & 0xff;
260  _buffer[index] = this.requestId & 0xff;
261  index = index + 4;
262
263  // index = write32bit(index, _buffer, 0);
264  _buffer[index + 3] = (0 >> 24) & 0xff;
265  _buffer[index + 2] = (0 >> 16) & 0xff;
266  _buffer[index + 1] = (0 >> 8) & 0xff;
267  _buffer[index] = 0 & 0xff;
268  index = index + 4;
269
270  // index = write32bit(index, _buffer, OP_GETMORE);
271  _buffer[index + 3] = (opcodes.OP_GETMORE >> 24) & 0xff;
272  _buffer[index + 2] = (opcodes.OP_GETMORE >> 16) & 0xff;
273  _buffer[index + 1] = (opcodes.OP_GETMORE >> 8) & 0xff;
274  _buffer[index] = opcodes.OP_GETMORE & 0xff;
275  index = index + 4;
276
277  // index = write32bit(index, _buffer, 0);
278  _buffer[index + 3] = (0 >> 24) & 0xff;
279  _buffer[index + 2] = (0 >> 16) & 0xff;
280  _buffer[index + 1] = (0 >> 8) & 0xff;
281  _buffer[index] = 0 & 0xff;
282  index = index + 4;
283
284  // Write collection name
285  index = index + _buffer.write(this.ns, index, 'utf8') + 1;
286  _buffer[index - 1] = 0;
287
288  // Write batch size
289  // index = write32bit(index, _buffer, numberToReturn);
290  _buffer[index + 3] = (this.numberToReturn >> 24) & 0xff;
291  _buffer[index + 2] = (this.numberToReturn >> 16) & 0xff;
292  _buffer[index + 1] = (this.numberToReturn >> 8) & 0xff;
293  _buffer[index] = this.numberToReturn & 0xff;
294  index = index + 4;
295
296  // Write cursor id
297  // index = write32bit(index, _buffer, cursorId.getLowBits());
298  _buffer[index + 3] = (this.cursorId.getLowBits() >> 24) & 0xff;
299  _buffer[index + 2] = (this.cursorId.getLowBits() >> 16) & 0xff;
300  _buffer[index + 1] = (this.cursorId.getLowBits() >> 8) & 0xff;
301  _buffer[index] = this.cursorId.getLowBits() & 0xff;
302  index = index + 4;
303
304  // index = write32bit(index, _buffer, cursorId.getHighBits());
305  _buffer[index + 3] = (this.cursorId.getHighBits() >> 24) & 0xff;
306  _buffer[index + 2] = (this.cursorId.getHighBits() >> 16) & 0xff;
307  _buffer[index + 1] = (this.cursorId.getHighBits() >> 8) & 0xff;
308  _buffer[index] = this.cursorId.getHighBits() & 0xff;
309  index = index + 4;
310
311  // Return buffer
312  return _buffer;
313};
314
315/**************************************************************
316 * KILLCURSOR
317 **************************************************************/
318var KillCursor = function(bson, cursorIds) {
319  this.requestId = _requestId++;
320  this.cursorIds = cursorIds;
321};
322
323//
324// Uses a single allocated buffer for the process, avoiding multiple memory allocations
325KillCursor.prototype.toBin = function() {
326  var length = 4 + 4 + 4 * 4 + this.cursorIds.length * 8;
327
328  // Create command buffer
329  var index = 0;
330  var _buffer = new Buffer(length);
331
332  // Write header information
333  // index = write32bit(index, _buffer, length);
334  _buffer[index + 3] = (length >> 24) & 0xff;
335  _buffer[index + 2] = (length >> 16) & 0xff;
336  _buffer[index + 1] = (length >> 8) & 0xff;
337  _buffer[index] = length & 0xff;
338  index = index + 4;
339
340  // index = write32bit(index, _buffer, requestId);
341  _buffer[index + 3] = (this.requestId >> 24) & 0xff;
342  _buffer[index + 2] = (this.requestId >> 16) & 0xff;
343  _buffer[index + 1] = (this.requestId >> 8) & 0xff;
344  _buffer[index] = this.requestId & 0xff;
345  index = index + 4;
346
347  // index = write32bit(index, _buffer, 0);
348  _buffer[index + 3] = (0 >> 24) & 0xff;
349  _buffer[index + 2] = (0 >> 16) & 0xff;
350  _buffer[index + 1] = (0 >> 8) & 0xff;
351  _buffer[index] = 0 & 0xff;
352  index = index + 4;
353
354  // index = write32bit(index, _buffer, OP_KILL_CURSORS);
355  _buffer[index + 3] = (opcodes.OP_KILL_CURSORS >> 24) & 0xff;
356  _buffer[index + 2] = (opcodes.OP_KILL_CURSORS >> 16) & 0xff;
357  _buffer[index + 1] = (opcodes.OP_KILL_CURSORS >> 8) & 0xff;
358  _buffer[index] = opcodes.OP_KILL_CURSORS & 0xff;
359  index = index + 4;
360
361  // index = write32bit(index, _buffer, 0);
362  _buffer[index + 3] = (0 >> 24) & 0xff;
363  _buffer[index + 2] = (0 >> 16) & 0xff;
364  _buffer[index + 1] = (0 >> 8) & 0xff;
365  _buffer[index] = 0 & 0xff;
366  index = index + 4;
367
368  // Write batch size
369  // index = write32bit(index, _buffer, this.cursorIds.length);
370  _buffer[index + 3] = (this.cursorIds.length >> 24) & 0xff;
371  _buffer[index + 2] = (this.cursorIds.length >> 16) & 0xff;
372  _buffer[index + 1] = (this.cursorIds.length >> 8) & 0xff;
373  _buffer[index] = this.cursorIds.length & 0xff;
374  index = index + 4;
375
376  // Write all the cursor ids into the array
377  for (var i = 0; i < this.cursorIds.length; i++) {
378    // Write cursor id
379    // index = write32bit(index, _buffer, cursorIds[i].getLowBits());
380    _buffer[index + 3] = (this.cursorIds[i].getLowBits() >> 24) & 0xff;
381    _buffer[index + 2] = (this.cursorIds[i].getLowBits() >> 16) & 0xff;
382    _buffer[index + 1] = (this.cursorIds[i].getLowBits() >> 8) & 0xff;
383    _buffer[index] = this.cursorIds[i].getLowBits() & 0xff;
384    index = index + 4;
385
386    // index = write32bit(index, _buffer, cursorIds[i].getHighBits());
387    _buffer[index + 3] = (this.cursorIds[i].getHighBits() >> 24) & 0xff;
388    _buffer[index + 2] = (this.cursorIds[i].getHighBits() >> 16) & 0xff;
389    _buffer[index + 1] = (this.cursorIds[i].getHighBits() >> 8) & 0xff;
390    _buffer[index] = this.cursorIds[i].getHighBits() & 0xff;
391    index = index + 4;
392  }
393
394  // Return buffer
395  return _buffer;
396};
397
398var Response = function(bson, message, msgHeader, msgBody, opts) {
399  opts = opts || { promoteLongs: true, promoteValues: true, promoteBuffers: false };
400  this.parsed = false;
401  this.raw = message;
402  this.data = msgBody;
403  this.bson = bson;
404  this.opts = opts;
405
406  // Read the message header
407  this.length = msgHeader.length;
408  this.requestId = msgHeader.requestId;
409  this.responseTo = msgHeader.responseTo;
410  this.opCode = msgHeader.opCode;
411  this.fromCompressed = msgHeader.fromCompressed;
412
413  // Read the message body
414  this.responseFlags = msgBody.readInt32LE(0);
415  this.cursorId = new Long(msgBody.readInt32LE(4), msgBody.readInt32LE(8));
416  this.startingFrom = msgBody.readInt32LE(12);
417  this.numberReturned = msgBody.readInt32LE(16);
418
419  // Preallocate document array
420  this.documents = new Array(this.numberReturned);
421
422  // Flag values
423  this.cursorNotFound = (this.responseFlags & CURSOR_NOT_FOUND) !== 0;
424  this.queryFailure = (this.responseFlags & QUERY_FAILURE) !== 0;
425  this.shardConfigStale = (this.responseFlags & SHARD_CONFIG_STALE) !== 0;
426  this.awaitCapable = (this.responseFlags & AWAIT_CAPABLE) !== 0;
427  this.promoteLongs = typeof opts.promoteLongs === 'boolean' ? opts.promoteLongs : true;
428  this.promoteValues = typeof opts.promoteValues === 'boolean' ? opts.promoteValues : true;
429  this.promoteBuffers = typeof opts.promoteBuffers === 'boolean' ? opts.promoteBuffers : false;
430};
431
432Response.prototype.isParsed = function() {
433  return this.parsed;
434};
435
436Response.prototype.parse = function(options) {
437  // Don't parse again if not needed
438  if (this.parsed) return;
439  options = options || {};
440
441  // Allow the return of raw documents instead of parsing
442  var raw = options.raw || false;
443  var documentsReturnedIn = options.documentsReturnedIn || null;
444  var promoteLongs =
445    typeof options.promoteLongs === 'boolean' ? options.promoteLongs : this.opts.promoteLongs;
446  var promoteValues =
447    typeof options.promoteValues === 'boolean' ? options.promoteValues : this.opts.promoteValues;
448  var promoteBuffers =
449    typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : this.opts.promoteBuffers;
450  var bsonSize, _options;
451
452  // Set up the options
453  _options = {
454    promoteLongs: promoteLongs,
455    promoteValues: promoteValues,
456    promoteBuffers: promoteBuffers
457  };
458
459  // Position within OP_REPLY at which documents start
460  // (See https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/#wire-op-reply)
461  this.index = 20;
462
463  //
464  // Single document and documentsReturnedIn set
465  //
466  if (this.numberReturned === 1 && documentsReturnedIn != null && raw) {
467    // Calculate the bson size
468    bsonSize =
469      this.data[this.index] |
470      (this.data[this.index + 1] << 8) |
471      (this.data[this.index + 2] << 16) |
472      (this.data[this.index + 3] << 24);
473    // Slice out the buffer containing the command result document
474    var document = this.data.slice(this.index, this.index + bsonSize);
475    // Set up field we wish to keep as raw
476    var fieldsAsRaw = {};
477    fieldsAsRaw[documentsReturnedIn] = true;
478    _options.fieldsAsRaw = fieldsAsRaw;
479
480    // Deserialize but keep the array of documents in non-parsed form
481    var doc = this.bson.deserialize(document, _options);
482
483    // Get the documents
484    this.documents = doc.cursor[documentsReturnedIn];
485    this.numberReturned = this.documents.length;
486    // Ensure we have a Long valie cursor id
487    this.cursorId =
488      typeof doc.cursor.id === 'number' ? Long.fromNumber(doc.cursor.id) : doc.cursor.id;
489
490    // Adjust the index
491    this.index = this.index + bsonSize;
492
493    // Set as parsed
494    this.parsed = true;
495    return;
496  }
497
498  //
499  // Parse Body
500  //
501  for (var i = 0; i < this.numberReturned; i++) {
502    bsonSize =
503      this.data[this.index] |
504      (this.data[this.index + 1] << 8) |
505      (this.data[this.index + 2] << 16) |
506      (this.data[this.index + 3] << 24);
507
508    // If we have raw results specified slice the return document
509    if (raw) {
510      this.documents[i] = this.data.slice(this.index, this.index + bsonSize);
511    } else {
512      this.documents[i] = this.bson.deserialize(
513        this.data.slice(this.index, this.index + bsonSize),
514        _options
515      );
516    }
517
518    // Adjust the index
519    this.index = this.index + bsonSize;
520  }
521
522  // Set parsed
523  this.parsed = true;
524};
525
526module.exports = {
527  Query: Query,
528  GetMore: GetMore,
529  Response: Response,
530  KillCursor: KillCursor
531};