mongo 2.14.0 → 2.15.0.alpha

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 (230) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +4 -1
  5. data/Rakefile +8 -15
  6. data/lib/mongo.rb +23 -0
  7. data/lib/mongo/auth/aws/conversation.rb +1 -4
  8. data/lib/mongo/auth/base.rb +13 -7
  9. data/lib/mongo/auth/conversation_base.rb +32 -0
  10. data/lib/mongo/auth/cr/conversation.rb +6 -29
  11. data/lib/mongo/auth/gssapi/conversation.rb +4 -15
  12. data/lib/mongo/auth/ldap/conversation.rb +3 -14
  13. data/lib/mongo/auth/sasl_conversation_base.rb +1 -13
  14. data/lib/mongo/auth/scram_conversation_base.rb +7 -34
  15. data/lib/mongo/auth/user/view.rb +16 -9
  16. data/lib/mongo/auth/x509/conversation.rb +4 -25
  17. data/lib/mongo/background_thread.rb +11 -0
  18. data/lib/mongo/bulk_write.rb +21 -18
  19. data/lib/mongo/client.rb +82 -6
  20. data/lib/mongo/cluster.rb +19 -28
  21. data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -2
  22. data/lib/mongo/cluster/sdam_flow.rb +14 -0
  23. data/lib/mongo/collection.rb +8 -6
  24. data/lib/mongo/collection/view/aggregation.rb +1 -1
  25. data/lib/mongo/collection/view/change_stream.rb +1 -1
  26. data/lib/mongo/collection/view/iterable.rb +1 -1
  27. data/lib/mongo/collection/view/map_reduce.rb +2 -2
  28. data/lib/mongo/collection/view/readable.rb +42 -20
  29. data/lib/mongo/collection/view/writable.rb +14 -14
  30. data/lib/mongo/cursor.rb +2 -2
  31. data/lib/mongo/database.rb +22 -5
  32. data/lib/mongo/database/view.rb +1 -1
  33. data/lib/mongo/error.rb +9 -1
  34. data/lib/mongo/error/bulk_write_error.rb +17 -3
  35. data/lib/mongo/error/internal_driver_error.rb +22 -0
  36. data/lib/mongo/error/operation_failure.rb +21 -2
  37. data/lib/mongo/error/parser.rb +65 -12
  38. data/lib/mongo/error/server_api_conflict.rb +23 -0
  39. data/lib/mongo/error/server_api_not_supported.rb +24 -0
  40. data/lib/mongo/error/unmet_dependency.rb +21 -0
  41. data/lib/mongo/grid/fs_bucket.rb +37 -37
  42. data/lib/mongo/index/view.rb +21 -11
  43. data/lib/mongo/monitoring.rb +13 -4
  44. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +27 -16
  45. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +26 -15
  46. data/lib/mongo/operation.rb +2 -2
  47. data/lib/mongo/operation/collections_info.rb +18 -1
  48. data/lib/mongo/operation/collections_info/command.rb +2 -2
  49. data/lib/mongo/operation/context.rb +99 -0
  50. data/lib/mongo/operation/indexes.rb +15 -1
  51. data/lib/mongo/operation/insert/command.rb +2 -2
  52. data/lib/mongo/operation/insert/legacy.rb +2 -2
  53. data/lib/mongo/operation/insert/op_msg.rb +2 -2
  54. data/lib/mongo/operation/list_collections/result.rb +4 -1
  55. data/lib/mongo/operation/result.rb +2 -0
  56. data/lib/mongo/operation/shared/executable.rb +24 -14
  57. data/lib/mongo/operation/shared/executable_no_validate.rb +2 -2
  58. data/lib/mongo/operation/shared/op_msg_or_command.rb +1 -7
  59. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +1 -7
  60. data/lib/mongo/operation/shared/polymorphic_operation.rb +39 -0
  61. data/lib/mongo/operation/shared/response_handling.rb +23 -23
  62. data/lib/mongo/operation/shared/sessions_supported.rb +13 -2
  63. data/lib/mongo/operation/shared/write.rb +8 -18
  64. data/lib/mongo/protocol/compressed.rb +51 -5
  65. data/lib/mongo/protocol/message.rb +20 -2
  66. data/lib/mongo/protocol/msg.rb +36 -11
  67. data/lib/mongo/query_cache.rb +30 -0
  68. data/lib/mongo/retryable.rb +1 -1
  69. data/lib/mongo/server.rb +7 -15
  70. data/lib/mongo/server/app_metadata.rb +52 -18
  71. data/lib/mongo/server/connection.rb +5 -0
  72. data/lib/mongo/server/connection_base.rb +13 -10
  73. data/lib/mongo/server/connection_pool.rb +6 -4
  74. data/lib/mongo/server/description.rb +4 -0
  75. data/lib/mongo/server/description/features.rb +9 -8
  76. data/lib/mongo/server/monitor.rb +20 -1
  77. data/lib/mongo/server/monitor/app_metadata.rb +1 -1
  78. data/lib/mongo/server/monitor/connection.rb +9 -10
  79. data/lib/mongo/server/pending_connection.rb +24 -6
  80. data/lib/mongo/server/push_monitor.rb +11 -1
  81. data/lib/mongo/session.rb +2 -2
  82. data/lib/mongo/session/session_pool.rb +4 -2
  83. data/lib/mongo/socket.rb +29 -4
  84. data/lib/mongo/socket/ssl.rb +8 -0
  85. data/lib/mongo/srv/monitor.rb +0 -11
  86. data/lib/mongo/uri/options_mapper.rb +38 -0
  87. data/lib/mongo/utils.rb +15 -0
  88. data/lib/mongo/version.rb +1 -1
  89. data/spec/README.md +24 -1
  90. data/spec/integration/auth_spec.rb +25 -15
  91. data/spec/integration/bulk_write_error_message_spec.rb +41 -0
  92. data/spec/integration/change_stream_spec.rb +4 -4
  93. data/spec/integration/command_monitoring_spec.rb +2 -2
  94. data/spec/integration/connection_spec.rb +2 -0
  95. data/spec/integration/docs_examples_spec.rb +8 -1
  96. data/spec/integration/fork_reconnect_spec.rb +4 -1
  97. data/spec/integration/ocsp_verifier_spec.rb +13 -7
  98. data/spec/integration/operation_failure_code_spec.rb +1 -1
  99. data/spec/integration/operation_failure_message_spec.rb +90 -0
  100. data/spec/integration/reconnect_spec.rb +1 -1
  101. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  102. data/spec/integration/sdam_events_spec.rb +3 -5
  103. data/spec/integration/snappy_compression_spec.rb +25 -0
  104. data/spec/integration/srv_monitoring_spec.rb +1 -1
  105. data/spec/integration/transactions_examples_spec.rb +6 -0
  106. data/spec/integration/zlib_compression_spec.rb +1 -1
  107. data/spec/integration/zstd_compression_spec.rb +26 -0
  108. data/spec/lite_spec_helper.rb +7 -1
  109. data/spec/mongo/address_spec.rb +15 -11
  110. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  111. data/spec/mongo/auth/ldap_spec.rb +5 -1
  112. data/spec/mongo/auth/scram_negotiation_spec.rb +1 -1
  113. data/spec/mongo/auth/scram_spec.rb +1 -1
  114. data/spec/mongo/auth/x509/conversation_spec.rb +3 -3
  115. data/spec/mongo/client_construction_spec.rb +207 -33
  116. data/spec/mongo/client_spec.rb +17 -0
  117. data/spec/mongo/cluster_spec.rb +3 -18
  118. data/spec/mongo/collection/view/explainable_spec.rb +1 -1
  119. data/spec/mongo/collection/view/readable_spec.rb +33 -19
  120. data/spec/mongo/collection_crud_spec.rb +4357 -0
  121. data/spec/mongo/collection_ddl_spec.rb +534 -0
  122. data/spec/mongo/collection_spec.rb +5 -4859
  123. data/spec/mongo/database_spec.rb +66 -4
  124. data/spec/mongo/error/bulk_write_error_spec.rb +3 -3
  125. data/spec/mongo/error/parser_spec.rb +37 -6
  126. data/spec/mongo/index/view_spec.rb +8 -2
  127. data/spec/mongo/monitoring/event/server_heartbeat_failed_spec.rb +1 -1
  128. data/spec/mongo/monitoring/event/server_heartbeat_succeeded_spec.rb +1 -1
  129. data/spec/mongo/operation/aggregate_spec.rb +2 -1
  130. data/spec/mongo/operation/collections_info_spec.rb +4 -1
  131. data/spec/mongo/operation/command_spec.rb +6 -3
  132. data/spec/mongo/operation/create_index_spec.rb +6 -3
  133. data/spec/mongo/operation/create_user_spec.rb +6 -3
  134. data/spec/mongo/operation/delete/bulk_spec.rb +9 -6
  135. data/spec/mongo/operation/delete_spec.rb +11 -7
  136. data/spec/mongo/operation/drop_index_spec.rb +6 -2
  137. data/spec/mongo/operation/find/legacy_spec.rb +3 -1
  138. data/spec/mongo/operation/get_more_spec.rb +3 -1
  139. data/spec/mongo/operation/indexes_spec.rb +5 -1
  140. data/spec/mongo/operation/insert/bulk_spec.rb +10 -7
  141. data/spec/mongo/operation/insert_spec.rb +15 -12
  142. data/spec/mongo/operation/map_reduce_spec.rb +5 -2
  143. data/spec/mongo/operation/remove_user_spec.rb +6 -3
  144. data/spec/mongo/operation/result_spec.rb +1 -1
  145. data/spec/mongo/operation/update/bulk_spec.rb +9 -6
  146. data/spec/mongo/operation/update_spec.rb +10 -7
  147. data/spec/mongo/operation/update_user_spec.rb +4 -1
  148. data/spec/mongo/protocol/compressed_spec.rb +26 -12
  149. data/spec/mongo/query_cache_middleware_spec.rb +55 -0
  150. data/spec/mongo/retryable_spec.rb +3 -2
  151. data/spec/mongo/server/app_metadata_spec.rb +2 -0
  152. data/spec/mongo/server/connection_pool/populator_spec.rb +3 -1
  153. data/spec/mongo/server/connection_pool_spec.rb +1 -1
  154. data/spec/mongo/server/connection_spec.rb +24 -17
  155. data/spec/mongo/server/monitor/connection_spec.rb +17 -7
  156. data/spec/mongo/server/monitor_spec.rb +9 -1
  157. data/spec/mongo/server_spec.rb +15 -2
  158. data/spec/mongo/socket/ssl_spec.rb +40 -0
  159. data/spec/mongo/socket_spec.rb +2 -2
  160. data/spec/mongo/tls_context_hooks_spec.rb +37 -0
  161. data/spec/runners/connection_string.rb +0 -4
  162. data/spec/runners/crud/requirement.rb +40 -3
  163. data/spec/runners/crud/verifier.rb +8 -0
  164. data/spec/runners/transactions/operation.rb +13 -2
  165. data/spec/runners/transactions/test.rb +1 -0
  166. data/spec/runners/unified.rb +96 -0
  167. data/spec/runners/unified/assertions.rb +249 -0
  168. data/spec/runners/unified/change_stream_operations.rb +26 -0
  169. data/spec/runners/unified/crud_operations.rb +199 -0
  170. data/spec/runners/unified/ddl_operations.rb +96 -0
  171. data/spec/runners/unified/entity_map.rb +39 -0
  172. data/spec/runners/unified/error.rb +25 -0
  173. data/spec/runners/unified/event_subscriber.rb +91 -0
  174. data/spec/runners/unified/exceptions.rb +21 -0
  175. data/spec/runners/unified/grid_fs_operations.rb +55 -0
  176. data/spec/runners/unified/support_operations.rb +250 -0
  177. data/spec/runners/unified/test.rb +393 -0
  178. data/spec/runners/unified/test_group.rb +28 -0
  179. data/spec/runners/unified/using_hash.rb +31 -0
  180. data/spec/shared/bin/get-mongodb-download-url +17 -0
  181. data/spec/shared/lib/mrss/cluster_config.rb +218 -0
  182. data/spec/shared/lib/mrss/constraints.rb +43 -0
  183. data/spec/shared/lib/mrss/docker_runner.rb +262 -0
  184. data/spec/shared/lib/mrss/server_version_registry.rb +112 -0
  185. data/spec/shared/lib/mrss/utils.rb +15 -0
  186. data/spec/shared/share/Dockerfile.erb +231 -0
  187. data/spec/shared/shlib/distro.sh +73 -0
  188. data/spec/shared/shlib/server.sh +290 -0
  189. data/spec/shared/shlib/set_env.sh +128 -0
  190. data/spec/solo/clean_exit_spec.rb +21 -0
  191. data/spec/spec_helper.rb +4 -1
  192. data/spec/spec_tests/crud_unified_spec.rb +10 -0
  193. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  194. data/spec/spec_tests/data/crud_unified/estimatedDocumentCount.yml +267 -0
  195. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-4.9.yml +60 -0
  196. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount.yml → estimatedDocumentCount-pre4.9.yml} +2 -0
  197. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors-4.9.yml +146 -0
  198. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount-serverErrors.yml → estimatedDocumentCount-serverErrors-pre4.9.yml} +2 -0
  199. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +1 -1
  200. data/spec/spec_tests/data/unified/valid-fail/operation-failure.yml +31 -0
  201. data/spec/spec_tests/data/unified/valid-pass/poc-change-streams.yml +220 -0
  202. data/spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml +102 -0
  203. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +184 -0
  204. data/spec/spec_tests/data/unified/valid-pass/poc-gridfs.yml +155 -0
  205. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml +193 -0
  206. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +210 -0
  207. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +215 -0
  208. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +235 -0
  209. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +169 -0
  210. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +170 -0
  211. data/spec/spec_tests/data/uri_options/compression-options.yml +1 -1
  212. data/spec/spec_tests/data/versioned_api/crud-api-version-1-strict.yml +416 -0
  213. data/spec/spec_tests/data/versioned_api/crud-api-version-1.yml +409 -0
  214. data/spec/spec_tests/data/versioned_api/runcommand-helper-no-api-version-declared.yml +67 -0
  215. data/spec/spec_tests/data/versioned_api/test-commands-deprecation-errors.yml +47 -0
  216. data/spec/spec_tests/data/versioned_api/test-commands-strict-mode.yml +44 -0
  217. data/spec/spec_tests/data/versioned_api/transaction-handling.yml +180 -0
  218. data/spec/spec_tests/unified_spec.rb +15 -0
  219. data/spec/spec_tests/uri_options_spec.rb +16 -0
  220. data/spec/spec_tests/versioned_api_spec.rb +10 -0
  221. data/spec/support/common_shortcuts.rb +15 -1
  222. data/spec/support/shared/session.rb +2 -2
  223. data/spec/support/spec_config.rb +46 -3
  224. data/spec/support/spec_setup.rb +48 -38
  225. data/spec/support/utils.rb +64 -3
  226. metadata +1104 -992
  227. metadata.gz.sig +0 -0
  228. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +0 -58
  229. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +0 -47
  230. data/spec/support/cluster_config.rb +0 -207
@@ -21,8 +21,8 @@ module Mongo
21
21
  # @api private
22
22
  module ExecutableNoValidate
23
23
 
24
- def execute(connection, client:)
25
- do_execute(connection, client)
24
+ def execute(connection, context:)
25
+ do_execute(connection, context)
26
26
  end
27
27
  end
28
28
  end
@@ -20,15 +20,9 @@ module Mongo
20
20
  #
21
21
  # @api private
22
22
  module OpMsgOrCommand
23
+ include PolymorphicOperation
23
24
  include PolymorphicLookup
24
25
 
25
- def execute(server, client:, options: {})
26
- server.with_connection do |connection|
27
- operation = final_operation(connection)
28
- operation.execute(connection, client: client, options: options)
29
- end
30
- end
31
-
32
26
  private
33
27
 
34
28
  def final_operation(connection)
@@ -21,15 +21,9 @@ module Mongo
21
21
  #
22
22
  # @api private
23
23
  module OpMsgOrFindCommand
24
+ include PolymorphicOperation
24
25
  include PolymorphicLookup
25
26
 
26
- def execute(server, client:)
27
- server.with_connection do |connection|
28
- operation = final_operation(connection)
29
- operation.execute(connection, client: client)
30
- end
31
- end
32
-
33
27
  private
34
28
 
35
29
  def final_operation(connection)
@@ -0,0 +1,39 @@
1
+ # Copyright (C) 2021 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Operation
17
+
18
+ # Shared behavior of implementing an operation differently based on
19
+ # the server that will be executing the operation.
20
+ #
21
+ # @api private
22
+ module PolymorphicOperation
23
+
24
+ # Execute the operation.
25
+ #
26
+ # @param [ Mongo::Server ] server The server to send the operation to.
27
+ # @param [ Operation::Context ] context The operation context.
28
+ # @param [ Hash ] options Operation execution options.
29
+ #
30
+ # @return [ Mongo::Operation::Result ] The operation result.
31
+ def execute(server, context:, options: {})
32
+ server.with_connection do |connection|
33
+ operation = final_operation(connection)
34
+ operation.execute(connection, context: context, options: options)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -22,9 +22,13 @@ module Mongo
22
22
 
23
23
  private
24
24
 
25
- def validate_result(result, client, connection)
26
- unpin_maybe(session) do
27
- add_error_labels(client, connection, session) do
25
+ # @param [ Mongo::Operation::Result ] result The operation result.
26
+ # @param [ Mongo::Server::Connection ] connection The connection on which
27
+ # the operation is performed.
28
+ # @param [ Mongo::Operation::Context ] context The operation context.
29
+ def validate_result(result, connection, context)
30
+ unpin_maybe(context.session) do
31
+ add_error_labels(connection, context) do
28
32
  add_server_diagnostics(connection) do
29
33
  result.validate!
30
34
  end
@@ -38,25 +42,29 @@ module Mongo
38
42
  # and server-side errors (Error::OperationFailure); it does not
39
43
  # handle server selection errors (Error::NoServerAvailable), for which
40
44
  # labels are added in the server selection code.
41
- def add_error_labels(client, connection, session)
45
+ #
46
+ # @param [ Mongo::Server::Connection ] connection The connection on which
47
+ # the operation is performed.
48
+ # @param [ Mongo::Operation::Context ] context The operation context.
49
+ def add_error_labels(connection, context)
42
50
  begin
43
51
  yield
44
52
  rescue Mongo::Error::SocketError => e
45
- if session && session.in_transaction? && !session.committing_transaction?
53
+ if context.in_transaction? && !context.committing_transaction?
46
54
  e.add_label('TransientTransactionError')
47
55
  end
48
- if session && session.committing_transaction?
56
+ if context.committing_transaction?
49
57
  e.add_label('UnknownTransactionCommitResult')
50
58
  end
51
59
 
52
- maybe_add_retryable_write_error_label!(e, connection, client, session)
60
+ maybe_add_retryable_write_error_label!(e, connection, context)
53
61
 
54
62
  raise e
55
63
  rescue Mongo::Error::SocketTimeoutError => e
56
- maybe_add_retryable_write_error_label!(e, connection, client, session)
64
+ maybe_add_retryable_write_error_label!(e, connection, context)
57
65
  raise e
58
66
  rescue Mongo::Error::OperationFailure => e
59
- if session && session.committing_transaction?
67
+ if context.committing_transaction?
60
68
  if e.write_retryable? || e.wtimeout? || (e.write_concern_error? &&
61
69
  !Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code)
62
70
  ) || e.max_time_ms_expired?
@@ -64,7 +72,7 @@ module Mongo
64
72
  end
65
73
  end
66
74
 
67
- maybe_add_retryable_write_error_label!(e, connection, client, session)
75
+ maybe_add_retryable_write_error_label!(e, connection, context)
68
76
 
69
77
  raise e
70
78
  end
@@ -125,21 +133,12 @@ module Mongo
125
133
  # @param [ Mongo::Error ] error The error to which to add the label.
126
134
  # @param [ Mongo::Server::Connection ] connection The connection on which
127
135
  # the operation is performed.
128
- # @param [ Mongo::Client | nil ] client The client that is performing
129
- # the operation.
130
- # @param [ Mongo::Session ] session The operation's session.
136
+ # @param [ Mongo::Operation::Context ] context The operation context.
131
137
  #
132
138
  # @note The client argument is optional because some operations, such as
133
139
  # end_session, do not pass the client as an argument to the execute
134
140
  # method.
135
- def maybe_add_retryable_write_error_label!(error, connection, client, session)
136
- in_transaction = session && session.in_transaction?
137
- committing_transaction = in_transaction && session.committing_transaction?
138
- aborting_transaction = in_transaction && session.aborting_transaction?
139
- modern_retry_writes = client && client.options[:retry_writes]
140
- legacy_retry_writes = client && !client.options[:retry_writes] &&
141
- client.max_write_retries > 0
142
-
141
+ def maybe_add_retryable_write_error_label!(error, connection, context)
143
142
  # An operation is retryable if it meets one of the following criteria:
144
143
  # - It is a commitTransaction or abortTransaction
145
144
  # - It does not occur during a transaction and the client has enabled
@@ -147,8 +146,9 @@ module Mongo
147
146
  #
148
147
  # Note: any write operation within a transaction (excepting commit and
149
148
  # abort is NOT a retryable operation)
150
- retryable_operation = committing_transaction || aborting_transaction ||
151
- (!in_transaction && (modern_retry_writes || legacy_retry_writes))
149
+ retryable_operation = context.committing_transaction? ||
150
+ context.aborting_transaction? ||
151
+ !context.in_transaction? && context.any_retry_writes?
152
152
 
153
153
  # An operation should add the RetryableWriteError label if one of the
154
154
  # following conditions is met:
@@ -222,9 +222,20 @@ module Mongo
222
222
  end
223
223
  end
224
224
 
225
- def build_message(connection)
225
+ def build_message(connection, context)
226
+ if self.session != context.session
227
+ if self.session
228
+ raise Error::InternalDriverError, "Operation session #{self.session.inspect} does not match context session #{context.session.inspect}"
229
+ else
230
+ # Some operations are not constructed with sessions but are
231
+ # executed in a context where a session is available.
232
+ # This could be OK or a driver issue.
233
+ # TODO investigate.
234
+ end
235
+ end
236
+
226
237
  super.tap do |message|
227
- if session
238
+ if session = context.session
228
239
  # Serialize the message to detect client-side problems,
229
240
  # such as invalid BSON keys. The message will be serialized again
230
241
  # later prior to being sent to the connection.
@@ -25,18 +25,13 @@ module Mongo
25
25
 
26
26
  # Execute the operation.
27
27
  #
28
- # @example
29
- # operation.execute(server, client: nil)
30
- #
31
28
  # @param [ Mongo::Server ] server The server to send the operation to.
32
- # @param [ Mongo::Client ] client The client that will be used to
33
- # perform auto-encryption if it is necessary to encrypt the command
34
- # being executed (optional).
29
+ # @param [ Operation::Context ] context The operation context.
35
30
  #
36
31
  # @return [ Mongo::Operation::Result ] The operation result.
37
32
  #
38
33
  # @since 2.5.2
39
- def execute(server, client:)
34
+ def execute(server, context:)
40
35
  server.with_connection do |connection|
41
36
  validate!(connection)
42
37
  op = if connection.features.op_msg_enabled?
@@ -47,34 +42,29 @@ module Mongo
47
42
  self.class::Command.new(spec)
48
43
  end
49
44
 
50
- result = op.execute(connection, client: client)
51
- validate_result(result, client, connection)
45
+ result = op.execute(connection, context: context)
46
+ validate_result(result, connection, context)
52
47
  end
53
48
  end
54
49
 
55
50
  # Execute the bulk write operation.
56
51
  #
57
- # @example
58
- # operation.bulk_execute(connection, client: nil)
59
- #
60
52
  # @param [ Mongo::Server::Connection ] connection The connection over
61
53
  # which to send the operation.
62
- # @param [ Mongo::Client ] client The client that will be used to
63
- # perform auto-encryption if it is necessary to encrypt the command
64
- # being executed (optional).
54
+ # @param [ Operation::Context ] context The operation context.
65
55
  #
66
56
  # @return [ Mongo::Operation::Delete::BulkResult,
67
57
  # Mongo::Operation::Insert::BulkResult,
68
58
  # Mongo::Operation::Update::BulkResult ] The bulk result.
69
59
  #
70
60
  # @since 2.5.2
71
- def bulk_execute(connection, client:)
61
+ def bulk_execute(connection, context:)
72
62
  Lint.assert_type(connection, Server::Connection)
73
63
 
74
64
  if connection.features.op_msg_enabled?
75
- self.class::OpMsg.new(spec).execute(connection, client: client).bulk_result
65
+ self.class::OpMsg.new(spec).execute(connection, context: context).bulk_result
76
66
  else
77
- self.class::Command.new(spec).execute(connection, client: client).bulk_result
67
+ self.class::Command.new(spec).execute(connection, context: context).bulk_result
78
68
  end
79
69
  end
80
70
 
@@ -18,12 +18,25 @@ module Mongo
18
18
  # MongoDB Wire protocol Compressed message.
19
19
  #
20
20
  # This is a bi-directional message that compresses another opcode.
21
+ # See https://github.com/mongodb/specifications/blob/master/source/compression/OP_COMPRESSED.rst
21
22
  #
22
23
  # @api semipublic
23
24
  #
24
25
  # @since 2.5.0
25
26
  class Compressed < Message
26
27
 
28
+ # The noop compressor identifier.
29
+ NOOP = 'noop'.freeze
30
+
31
+ # The byte signaling that the message has not been compressed (test mode).
32
+ NOOP_BYTE = 0.chr.force_encoding(BSON::BINARY).freeze
33
+
34
+ # The snappy compressor identifier.
35
+ SNAPPY = 'snappy'.freeze
36
+
37
+ # The byte signaling that the message has been compressed with snappy.
38
+ SNAPPY_BYTE = 1.chr.force_encoding(BSON::BINARY).freeze
39
+
27
40
  # The byte signaling that the message has been compressed with Zlib.
28
41
  #
29
42
  # @since 2.5.0
@@ -34,10 +47,20 @@ module Mongo
34
47
  # @since 2.5.0
35
48
  ZLIB = 'zlib'.freeze
36
49
 
50
+ # The zstd compressor identifier.
51
+ ZSTD = 'zstd'.freeze
52
+
53
+ # The byte signaling that the message has been compressed with zstd.
54
+ ZSTD_BYTE = 3.chr.force_encoding(BSON::BINARY).freeze
55
+
37
56
  # The compressor identifier to byte map.
38
57
  #
39
58
  # @since 2.5.0
40
- COMPRESSOR_ID_MAP = { ZLIB => ZLIB_BYTE }.freeze
59
+ COMPRESSOR_ID_MAP = {
60
+ SNAPPY => SNAPPY_BYTE,
61
+ ZSTD => ZSTD_BYTE,
62
+ ZLIB => ZLIB_BYTE
63
+ }.freeze
41
64
 
42
65
  # Creates a new OP_COMPRESSED message.
43
66
  #
@@ -68,9 +91,7 @@ module Mongo
68
91
  # @api private
69
92
  def maybe_inflate
70
93
  message = Registry.get(@original_op_code).allocate
71
- uncompressed_message = Zlib::Inflate.inflate(@compressed_message)
72
-
73
- buf = BSON::ByteBuffer.new(uncompressed_message)
94
+ buf = decompress(@compressed_message)
74
95
 
75
96
  message.send(:fields).each do |field|
76
97
  if field[:multi]
@@ -125,10 +146,35 @@ module Mongo
125
146
  buf = BSON::ByteBuffer.new
126
147
  @original_message.send(:serialize_fields, buf, max_bson_size)
127
148
  @uncompressed_size = buf.length
128
- @compressed_message = Zlib::Deflate.deflate(buf.to_s, @zlib_compression_level).force_encoding(BSON::BINARY)
149
+ @compressed_message = compress(buf)
129
150
  super
130
151
  end
131
152
 
153
+ def compress(buffer)
154
+ if @compressor_id == NOOP_BYTE
155
+ buffer.to_s.force_encoding(BSON::BINARY)
156
+ elsif @compressor_id == ZLIB_BYTE
157
+ Zlib::Deflate.deflate(buffer.to_s, @zlib_compression_level).force_encoding(BSON::BINARY)
158
+ elsif @compressor_id == SNAPPY_BYTE
159
+ Snappy.deflate(buffer.to_s).force_encoding(BSON::BINARY)
160
+ elsif @compressor_id == ZSTD_BYTE
161
+ # DRIVERS-600 will allow this to be configurable in the future
162
+ Zstd.compress(buffer.to_s).force_encoding(BSON::BINARY)
163
+ end
164
+ end
165
+
166
+ def decompress(compressed_message)
167
+ if @compressor_id == NOOP_BYTE
168
+ BSON::ByteBuffer.new(compressed_message)
169
+ elsif @compressor_id == ZLIB_BYTE
170
+ BSON::ByteBuffer.new(Zlib::Inflate.inflate(compressed_message))
171
+ elsif @compressor_id == SNAPPY_BYTE
172
+ BSON::ByteBuffer.new(Snappy.inflate(compressed_message))
173
+ elsif @compressor_id == ZSTD_BYTE
174
+ BSON::ByteBuffer.new(Zstd.decompress(compressed_message))
175
+ end
176
+ end
177
+
132
178
  Registry.register(OP_CODE, self)
133
179
  end
134
180
  end
@@ -140,7 +140,13 @@ module Mongo
140
140
  self
141
141
  end
142
142
 
143
- def maybe_decrypt(client)
143
+ # Possibly decrypt this message with libmongocrypt.
144
+ #
145
+ # @param [ Mongo::Operation::Context ] context The operation context.
146
+ #
147
+ # @return [ Mongo::Protocol::Msg ] The decrypted message, or the original
148
+ # message if decryption was not possible or necessary.
149
+ def maybe_decrypt(context)
144
150
  # TODO determine if we should be decrypting data coming from pre-4.2
145
151
  # servers, potentially using legacy wire protocols. If so we need
146
152
  # to implement decryption for those wire protocols as our current
@@ -148,11 +154,23 @@ module Mongo
148
154
  self
149
155
  end
150
156
 
151
- def maybe_encrypt(server, client)
157
+ # Possibly encrypt this message with libmongocrypt.
158
+ #
159
+ # @param [ Mongo::Server::Connection ] connection The connection on which
160
+ # the operation is performed.
161
+ # @param [ Mongo::Operation::Context ] context The operation context.
162
+ #
163
+ # @return [ Mongo::Protocol::Msg ] The encrypted message, or the original
164
+ # message if encryption was not possible or necessary.
165
+ def maybe_encrypt(connection, context)
152
166
  # Do nothing if the Message subclass has not implemented this method
153
167
  self
154
168
  end
155
169
 
170
+ def maybe_add_server_api(server_api)
171
+ raise Error::ServerApiNotSupported, "Server API parameters cannot be sent to pre-3.6 MongoDB servers. Please remove the :server_api parameter from Client options or use MongoDB 3.6 or newer"
172
+ end
173
+
156
174
  private def merge_sections
157
175
  cmd = if @sections.length > 1
158
176
  cmd = @sections.detect { |section| section[:type] == 0 }[:payload]
@@ -199,15 +199,16 @@ module Mongo
199
199
  # represents one of the command types whitelisted by libmongocrypt and it
200
200
  # contains data that is required to be encrypted by a local or remote json schema.
201
201
  #
202
- # @param [ Mongo::Client | nil ] client The client used to make the original
203
- # command. This client may have auto-encryption options specified.
202
+ # @param [ Mongo::Server::Connection ] connection The connection on which
203
+ # the operation is performed.
204
+ # @param [ Mongo::Operation::Context ] context The operation context.
204
205
  #
205
206
  # @return [ Mongo::Protocol::Msg ] The encrypted message, or the original
206
207
  # message if encryption was not possible or necessary.
207
- def maybe_encrypt(connection, client)
208
+ def maybe_encrypt(connection, context)
208
209
  # TODO verify compression happens later, i.e. when this method runs
209
210
  # the message is not compressed.
210
- if client && client.encrypter && client.encrypter.encrypt?
211
+ if context.encrypt?
211
212
  if connection.description.max_wire_version < 8
212
213
  raise Error::CryptError.new(
213
214
  "Cannot perform encryption against a MongoDB server older than " +
@@ -219,7 +220,7 @@ module Mongo
219
220
 
220
221
  db_name = @main_document[DATABASE_IDENTIFIER]
221
222
  cmd = merge_sections
222
- enc_cmd = client.encrypter.encrypt(db_name, cmd)
223
+ enc_cmd = context.encrypter.encrypt(db_name, cmd)
223
224
  if cmd.key?('$db') && !enc_cmd.key?('$db')
224
225
  enc_cmd['$db'] = cmd['$db']
225
226
  end
@@ -237,15 +238,14 @@ module Mongo
237
238
  # types whitelisted by libmongocrypt and it contains data that is required
238
239
  # to be encrypted by a local or remote json schema.
239
240
  #
240
- # @param [ Mongo::Client | nil ] client The client used to make the original
241
- # command. This client may have auto-encryption options specified.
241
+ # @param [ Mongo::Operation::Context ] context The operation context.
242
242
  #
243
- # @return [ Mongo::Protocol::Msg ] The decryption message, or the original
243
+ # @return [ Mongo::Protocol::Msg ] The decrypted message, or the original
244
244
  # message if decryption was not possible or necessary.
245
- def maybe_decrypt(client)
246
- if client && client.encrypter
245
+ def maybe_decrypt(context)
246
+ if context.decrypt?
247
247
  cmd = merge_sections
248
- enc_cmd = client.encrypter.decrypt(cmd)
248
+ enc_cmd = context.encrypter.decrypt(cmd)
249
249
  Msg.new(@flags, @options, enc_cmd)
250
250
  else
251
251
  self
@@ -275,6 +275,31 @@ module Mongo
275
275
  num_inserts > 1 || num_updates > 1 || num_deletes > 1
276
276
  end
277
277
 
278
+ def maybe_add_server_api(server_api)
279
+ if @main_document[:getMore]
280
+ # getMore does not allow API parameters.
281
+ return self
282
+ end
283
+
284
+ conflicts = {}
285
+ %i(apiVersion apiStrict apiDeprecationErrors).each do |key|
286
+ if @main_document.key?(key)
287
+ conflicts[key] = @main_document[key]
288
+ end
289
+ if @main_document.key?(key.to_s)
290
+ conflicts[key] = @main_document[key.to_s]
291
+ end
292
+ end
293
+ unless conflicts.empty?
294
+ raise Error::ServerApiConflict, "The Client is configured with :server_api option but the operation provided the following conflicting parameters: #{conflicts.inspect}"
295
+ end
296
+
297
+ main_document = @main_document.merge(
298
+ Utils.transform_server_api(server_api)
299
+ )
300
+ Msg.new(@flags, @options, main_document, *@sequences)
301
+ end
302
+
278
303
  private
279
304
 
280
305
  # Validate that the documents in this message are all smaller than the