Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3_2_support.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. 'use strict';
  2. const Query = require('../connection/commands').Query;
  3. const retrieveBSON = require('../connection/utils').retrieveBSON;
  4. const MongoError = require('../error').MongoError;
  5. const MongoNetworkError = require('../error').MongoNetworkError;
  6. const getReadPreference = require('./shared').getReadPreference;
  7. const BSON = retrieveBSON();
  8. const Long = BSON.Long;
  9. const ReadPreference = require('../topologies/read_preference');
  10. const TxnState = require('../transactions').TxnState;
  11. const isMongos = require('./shared').isMongos;
  12. const databaseNamespace = require('./shared').databaseNamespace;
  13. const collectionNamespace = require('./shared').collectionNamespace;
  14. class WireProtocol {
  15. insert(server, ns, ops, options, callback) {
  16. executeWrite(this, server, 'insert', 'documents', ns, ops, options, callback);
  17. }
  18. update(server, ns, ops, options, callback) {
  19. executeWrite(this, server, 'update', 'updates', ns, ops, options, callback);
  20. }
  21. remove(server, ns, ops, options, callback) {
  22. executeWrite(this, server, 'delete', 'deletes', ns, ops, options, callback);
  23. }
  24. killCursor(server, ns, cursorState, callback) {
  25. callback = typeof callback === 'function' ? callback : () => {};
  26. const cursorId = cursorState.cursorId;
  27. const killCursorCmd = {
  28. killCursors: collectionNamespace(ns),
  29. cursors: [cursorId]
  30. };
  31. const options = {};
  32. if (typeof cursorState.session === 'object') options.session = cursorState.session;
  33. this.command(server, ns, killCursorCmd, options, (err, result) => {
  34. if (err) {
  35. return callback(err);
  36. }
  37. const response = result.message;
  38. if (response.cursorNotFound) {
  39. return callback(new MongoNetworkError('cursor killed or timed out'), null);
  40. }
  41. if (!Array.isArray(response.documents) || response.documents.length === 0) {
  42. return callback(
  43. new MongoError(`invalid killCursors result returned for cursor id ${cursorId}`)
  44. );
  45. }
  46. callback(null, response.documents[0]);
  47. });
  48. }
  49. getMore(server, ns, cursorState, batchSize, options, callback) {
  50. options = options || {};
  51. const getMoreCmd = {
  52. getMore: cursorState.cursorId,
  53. collection: collectionNamespace(ns),
  54. batchSize: Math.abs(batchSize)
  55. };
  56. if (cursorState.cmd.tailable && typeof cursorState.cmd.maxAwaitTimeMS === 'number') {
  57. getMoreCmd.maxTimeMS = cursorState.cmd.maxAwaitTimeMS;
  58. }
  59. function queryCallback(err, result) {
  60. if (err) return callback(err);
  61. const response = result.message;
  62. // If we have a timed out query or a cursor that was killed
  63. if (response.cursorNotFound) {
  64. return callback(new MongoNetworkError('cursor killed or timed out'), null);
  65. }
  66. // Raw, return all the extracted documents
  67. if (cursorState.raw) {
  68. cursorState.documents = response.documents;
  69. cursorState.cursorId = response.cursorId;
  70. return callback(null, response.documents);
  71. }
  72. // We have an error detected
  73. if (response.documents[0].ok === 0) {
  74. return callback(new MongoError(response.documents[0]));
  75. }
  76. // Ensure we have a Long valid cursor id
  77. const cursorId =
  78. typeof response.documents[0].cursor.id === 'number'
  79. ? Long.fromNumber(response.documents[0].cursor.id)
  80. : response.documents[0].cursor.id;
  81. cursorState.documents = response.documents[0].cursor.nextBatch;
  82. cursorState.cursorId = cursorId;
  83. callback(null, response.documents[0], response.connection);
  84. }
  85. const commandOptions = Object.assign(
  86. {
  87. returnFieldSelector: null,
  88. documentsReturnedIn: 'nextBatch'
  89. },
  90. options
  91. );
  92. this.command(server, ns, getMoreCmd, commandOptions, queryCallback);
  93. }
  94. query(server, ns, cmd, cursorState, options, callback) {
  95. options = options || {};
  96. if (cursorState.cursorId != null) {
  97. return callback();
  98. }
  99. if (cmd == null) {
  100. return callback(new MongoError(`command ${JSON.stringify(cmd)} does not return a cursor`));
  101. }
  102. const readPreference = getReadPreference(cmd, options);
  103. const findCmd = prepareFindCommand(server, ns, cmd, cursorState, options);
  104. // NOTE: This actually modifies the passed in cmd, and our code _depends_ on this
  105. // side-effect. Change this ASAP
  106. cmd.virtual = false;
  107. const commandOptions = Object.assign(
  108. {
  109. documentsReturnedIn: 'firstBatch',
  110. numberToReturn: 1,
  111. slaveOk: readPreference.slaveOk()
  112. },
  113. options
  114. );
  115. if (cmd.readPreference) commandOptions.readPreference = readPreference;
  116. this.command(server, ns, findCmd, commandOptions, callback);
  117. }
  118. command(server, ns, cmd, options, callback) {
  119. if (typeof options === 'function') (callback = options), (options = {});
  120. options = options || {};
  121. if (cmd == null) {
  122. return callback(new MongoError(`command ${JSON.stringify(cmd)} does not return a cursor`));
  123. }
  124. const bson = server.s.bson;
  125. const pool = server.s.pool;
  126. const readPreference = getReadPreference(cmd, options);
  127. let finalCmd = Object.assign({}, cmd);
  128. if (isMongos(server) && readPreference && readPreference.preference !== 'primary') {
  129. finalCmd = {
  130. $query: finalCmd,
  131. $readPreference: readPreference.toJSON()
  132. };
  133. }
  134. const err = decorateWithSessionsData(finalCmd, options.session, options);
  135. if (err) {
  136. return callback(err);
  137. }
  138. const commandOptions = Object.assign(
  139. {
  140. command: true,
  141. numberToSkip: 0,
  142. numberToReturn: -1,
  143. checkKeys: false
  144. },
  145. options
  146. );
  147. // This value is not overridable
  148. commandOptions.slaveOk = readPreference.slaveOk();
  149. try {
  150. const query = new Query(bson, `${databaseNamespace(ns)}.$cmd`, finalCmd, commandOptions);
  151. pool.write(query, commandOptions, callback);
  152. } catch (err) {
  153. callback(err);
  154. }
  155. }
  156. }
  157. function isTransactionCommand(command) {
  158. return !!(command.commitTransaction || command.abortTransaction);
  159. }
  160. /**
  161. * Optionally decorate a command with sessions specific keys
  162. *
  163. * @param {Object} command the command to decorate
  164. * @param {ClientSession} session the session tracking transaction state
  165. * @param {Object} [options] Optional settings passed to calling operation
  166. * @param {Function} [callback] Optional callback passed from calling operation
  167. * @return {MongoError|null} An error, if some error condition was met
  168. */
  169. function decorateWithSessionsData(command, session, options) {
  170. if (!session) {
  171. return;
  172. }
  173. // first apply non-transaction-specific sessions data
  174. const serverSession = session.serverSession;
  175. const inTransaction = session.inTransaction() || isTransactionCommand(command);
  176. const isRetryableWrite = options.willRetryWrite;
  177. if (serverSession.txnNumber && (isRetryableWrite || inTransaction)) {
  178. command.txnNumber = BSON.Long.fromNumber(serverSession.txnNumber);
  179. }
  180. // now attempt to apply transaction-specific sessions data
  181. if (!inTransaction) {
  182. if (session.transaction.state !== TxnState.NO_TRANSACTION) {
  183. session.transaction.transition(TxnState.NO_TRANSACTION);
  184. }
  185. // for causal consistency
  186. if (session.supports.causalConsistency && session.operationTime) {
  187. command.readConcern = command.readConcern || {};
  188. Object.assign(command.readConcern, { afterClusterTime: session.operationTime });
  189. }
  190. return;
  191. }
  192. if (options.readPreference && !options.readPreference.equals(ReadPreference.primary)) {
  193. return new MongoError(
  194. `Read preference in a transaction must be primary, not: ${options.readPreference.mode}`
  195. );
  196. }
  197. // `autocommit` must always be false to differentiate from retryable writes
  198. command.autocommit = false;
  199. if (session.transaction.state === TxnState.STARTING_TRANSACTION) {
  200. session.transaction.transition(TxnState.TRANSACTION_IN_PROGRESS);
  201. command.startTransaction = true;
  202. const readConcern =
  203. session.transaction.options.readConcern || session.clientOptions.readConcern;
  204. if (readConcern) {
  205. command.readConcern = readConcern;
  206. }
  207. if (session.supports.causalConsistency && session.operationTime) {
  208. command.readConcern = command.readConcern || {};
  209. Object.assign(command.readConcern, { afterClusterTime: session.operationTime });
  210. }
  211. }
  212. }
  213. function executeWrite(handler, server, type, opsField, ns, ops, options, callback) {
  214. if (ops.length === 0) throw new MongoError('insert must contain at least one document');
  215. if (typeof options === 'function') {
  216. callback = options;
  217. options = {};
  218. options = options || {};
  219. }
  220. const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
  221. const writeConcern = options.writeConcern;
  222. const writeCommand = {};
  223. writeCommand[type] = collectionNamespace(ns);
  224. writeCommand[opsField] = ops;
  225. writeCommand.ordered = ordered;
  226. if (writeConcern && Object.keys(writeConcern).length > 0) {
  227. writeCommand.writeConcern = writeConcern;
  228. }
  229. if (options.collation) {
  230. for (let i = 0; i < writeCommand[opsField].length; i++) {
  231. if (!writeCommand[opsField][i].collation) {
  232. writeCommand[opsField][i].collation = options.collation;
  233. }
  234. }
  235. }
  236. if (options.bypassDocumentValidation === true) {
  237. writeCommand.bypassDocumentValidation = options.bypassDocumentValidation;
  238. }
  239. const commandOptions = Object.assign(
  240. {
  241. checkKeys: type === 'insert',
  242. numberToReturn: 1
  243. },
  244. options
  245. );
  246. handler.command(server, ns, writeCommand, commandOptions, callback);
  247. }
  248. function prepareFindCommand(server, ns, cmd, cursorState) {
  249. cursorState.batchSize = cmd.batchSize || cursorState.batchSize;
  250. let findCmd = {
  251. find: collectionNamespace(ns)
  252. };
  253. if (cmd.query) {
  254. if (cmd.query['$query']) {
  255. findCmd.filter = cmd.query['$query'];
  256. } else {
  257. findCmd.filter = cmd.query;
  258. }
  259. }
  260. let sortValue = cmd.sort;
  261. if (Array.isArray(sortValue)) {
  262. const sortObject = {};
  263. if (sortValue.length > 0 && !Array.isArray(sortValue[0])) {
  264. let sortDirection = sortValue[1];
  265. if (sortDirection === 'asc') {
  266. sortDirection = 1;
  267. } else if (sortDirection === 'desc') {
  268. sortDirection = -1;
  269. }
  270. sortObject[sortValue[0]] = sortDirection;
  271. } else {
  272. for (let i = 0; i < sortValue.length; i++) {
  273. let sortDirection = sortValue[i][1];
  274. if (sortDirection === 'asc') {
  275. sortDirection = 1;
  276. } else if (sortDirection === 'desc') {
  277. sortDirection = -1;
  278. }
  279. sortObject[sortValue[i][0]] = sortDirection;
  280. }
  281. }
  282. sortValue = sortObject;
  283. }
  284. if (cmd.sort) findCmd.sort = sortValue;
  285. if (cmd.fields) findCmd.projection = cmd.fields;
  286. if (cmd.hint) findCmd.hint = cmd.hint;
  287. if (cmd.skip) findCmd.skip = cmd.skip;
  288. if (cmd.limit) findCmd.limit = cmd.limit;
  289. if (cmd.limit < 0) {
  290. findCmd.limit = Math.abs(cmd.limit);
  291. findCmd.singleBatch = true;
  292. }
  293. if (typeof cmd.batchSize === 'number') {
  294. if (cmd.batchSize < 0) {
  295. if (cmd.limit !== 0 && Math.abs(cmd.batchSize) < Math.abs(cmd.limit)) {
  296. findCmd.limit = Math.abs(cmd.batchSize);
  297. }
  298. findCmd.singleBatch = true;
  299. }
  300. findCmd.batchSize = Math.abs(cmd.batchSize);
  301. }
  302. if (cmd.comment) findCmd.comment = cmd.comment;
  303. if (cmd.maxScan) findCmd.maxScan = cmd.maxScan;
  304. if (cmd.maxTimeMS) findCmd.maxTimeMS = cmd.maxTimeMS;
  305. if (cmd.min) findCmd.min = cmd.min;
  306. if (cmd.max) findCmd.max = cmd.max;
  307. findCmd.returnKey = cmd.returnKey ? cmd.returnKey : false;
  308. findCmd.showRecordId = cmd.showDiskLoc ? cmd.showDiskLoc : false;
  309. if (cmd.snapshot) findCmd.snapshot = cmd.snapshot;
  310. if (cmd.tailable) findCmd.tailable = cmd.tailable;
  311. if (cmd.oplogReplay) findCmd.oplogReplay = cmd.oplogReplay;
  312. if (cmd.noCursorTimeout) findCmd.noCursorTimeout = cmd.noCursorTimeout;
  313. if (cmd.awaitData) findCmd.awaitData = cmd.awaitData;
  314. if (cmd.awaitdata) findCmd.awaitData = cmd.awaitdata;
  315. if (cmd.partial) findCmd.partial = cmd.partial;
  316. if (cmd.collation) findCmd.collation = cmd.collation;
  317. if (cmd.readConcern) findCmd.readConcern = cmd.readConcern;
  318. // If we have explain, we need to rewrite the find command
  319. // to wrap it in the explain command
  320. if (cmd.explain) {
  321. findCmd = {
  322. explain: findCmd
  323. };
  324. }
  325. return findCmd;
  326. }
  327. module.exports = WireProtocol;