couchbase 3.8.2 → 3.9.0

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 (473) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/CMakeLists.txt +16 -1
  4. data/ext/cache/cpm/{CPM_0.42.0.cmake → CPM_0.43.1.cmake} +18 -2
  5. data/ext/cache/extconf_include.rb +6 -6
  6. data/ext/cache/mozilla-ca-bundle.crt +121 -55
  7. data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
  8. data/ext/couchbase/CMakeLists.txt +130 -21
  9. data/ext/couchbase/cmake/APKBUILD.in +25 -4
  10. data/ext/couchbase/cmake/Bundler.cmake +143 -34
  11. data/ext/couchbase/cmake/CPM.cmake +126 -12
  12. data/ext/couchbase/cmake/Cache.cmake +7 -1
  13. data/ext/couchbase/cmake/CompilerOptions.cmake +1 -1
  14. data/ext/couchbase/cmake/CompilerWarnings.cmake +17 -6
  15. data/ext/couchbase/cmake/GrpcProtobuf.cmake +481 -0
  16. data/ext/couchbase/cmake/OpenSSL.cmake +10 -3
  17. data/ext/couchbase/cmake/Packaging.cmake +519 -65
  18. data/ext/couchbase/cmake/Protostellar.cmake +250 -0
  19. data/ext/couchbase/cmake/Sanitizers.cmake +61 -50
  20. data/ext/couchbase/cmake/StaticAnalyzers.cmake +16 -5
  21. data/ext/couchbase/cmake/TestFramework.cmake +283 -0
  22. data/ext/couchbase/cmake/TestFrameworkAddTests.cmake +51 -0
  23. data/ext/couchbase/cmake/Testing.cmake +27 -9
  24. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +141 -8
  25. data/ext/couchbase/cmake/VersionInfo.cmake +50 -0
  26. data/ext/couchbase/cmake/build_config.hxx.in +2 -1
  27. data/ext/couchbase/cmake/couchbase-cxx-client.spec.in +101 -23
  28. data/ext/couchbase/cmake/couchbase_cxx_client_static-config.cmake.in +7 -0
  29. data/ext/couchbase/cmake/couchbase_cxx_client_static.pc.in +2 -0
  30. data/ext/couchbase/cmake/gRPC_Generate.cmake +58 -0
  31. data/ext/couchbase/cmake/protobuf_Generate.cmake +69 -0
  32. data/ext/couchbase/cmake/require-git-checkout.cmake +25 -0
  33. data/ext/couchbase/cmake/tarball_glob.txt +49 -0
  34. data/ext/couchbase/core/agent_group.cxx +0 -8
  35. data/ext/couchbase/core/agent_group.hxx +0 -4
  36. data/ext/couchbase/core/analytics_stream.cxx +314 -0
  37. data/ext/couchbase/core/analytics_stream.hxx +129 -0
  38. data/ext/couchbase/core/analytics_stream_component.cxx +245 -0
  39. data/ext/couchbase/core/analytics_stream_component.hxx +67 -0
  40. data/ext/couchbase/core/app_telemetry_meter.cxx +25 -3
  41. data/ext/couchbase/core/app_telemetry_meter.hxx +77 -0
  42. data/ext/couchbase/core/app_telemetry_reporter.cxx +7 -6
  43. data/ext/couchbase/core/bucket.cxx +381 -87
  44. data/ext/couchbase/core/bucket.hxx +105 -25
  45. data/ext/couchbase/core/bucket_unit_test_api.hxx +57 -0
  46. data/ext/couchbase/core/capella.hxx +91 -0
  47. data/ext/couchbase/core/cluster.cxx +1554 -398
  48. data/ext/couchbase/core/cluster.hxx +39 -8
  49. data/ext/couchbase/core/cluster_agent.cxx +0 -8
  50. data/ext/couchbase/core/cluster_agent.hxx +0 -4
  51. data/ext/couchbase/core/cluster_options.hxx +26 -3
  52. data/ext/couchbase/core/collections_component.cxx +39 -15
  53. data/ext/couchbase/core/config_profiles.cxx +1 -1
  54. data/ext/couchbase/core/crud_component.cxx +2 -2
  55. data/ext/couchbase/core/crud_options.hxx +34 -0
  56. data/ext/couchbase/core/crypto/cbcrypto.cc +18 -13
  57. data/ext/couchbase/core/document_id_redaction.hxx +81 -0
  58. data/ext/couchbase/core/error_context/base_error_context.hxx +7 -6
  59. data/ext/couchbase/core/error_context/key_value.cxx +25 -2
  60. data/ext/couchbase/core/error_context/key_value.hxx +60 -33
  61. data/ext/couchbase/core/error_context/key_value_error_context.hxx +16 -15
  62. data/ext/couchbase/core/error_context/search.hxx +5 -0
  63. data/ext/couchbase/core/error_context/search_json.hxx +3 -0
  64. data/ext/couchbase/core/error_context/subdocument_error_context.hxx +19 -18
  65. data/ext/couchbase/core/error_context/view.hxx +5 -0
  66. data/ext/couchbase/core/free_form_http_request.cxx +237 -4
  67. data/ext/couchbase/core/free_form_http_request.hxx +41 -8
  68. data/ext/couchbase/core/http_component.cxx +300 -284
  69. data/ext/couchbase/core/http_component.hxx +0 -4
  70. data/ext/couchbase/core/impl/analytics.cxx +38 -5
  71. data/ext/couchbase/core/impl/analytics.hxx +34 -0
  72. data/ext/couchbase/core/impl/bootstrap_error.hxx +0 -3
  73. data/ext/couchbase/core/impl/collection.cxx +215 -202
  74. data/ext/couchbase/core/impl/collection_manager.cxx +1 -1
  75. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +45 -18
  76. data/ext/couchbase/core/impl/error.cxx +22 -2
  77. data/ext/couchbase/core/impl/internal_analytics_stream_result.hxx +111 -0
  78. data/ext/couchbase/core/impl/internal_query_stream_result.hxx +110 -0
  79. data/ext/couchbase/core/impl/invoke_with_node_id.hxx +15 -7
  80. data/ext/couchbase/core/impl/key_value_error_category.cxx +6 -0
  81. data/ext/couchbase/core/impl/lookup_in_replica.cxx +10 -10
  82. data/ext/couchbase/core/impl/lookup_in_replica.hxx +4 -4
  83. data/ext/couchbase/core/impl/node_id.cxx +49 -13
  84. data/ext/couchbase/core/impl/observability_recorder.cxx +12 -2
  85. data/ext/couchbase/core/impl/observe_poll.cxx +63 -23
  86. data/ext/couchbase/core/impl/public_analytics_stream_result.cxx +372 -0
  87. data/ext/couchbase/core/impl/public_bucket.cxx +48 -2
  88. data/ext/couchbase/core/impl/public_cluster.cxx +229 -42
  89. data/ext/couchbase/core/impl/public_query_stream_result.cxx +380 -0
  90. data/ext/couchbase/core/impl/public_transaction_get_multi_replicas_from_preferred_server_group_result.cxx +42 -0
  91. data/ext/couchbase/core/impl/public_transaction_get_multi_result.cxx +42 -0
  92. data/ext/couchbase/core/impl/query.cxx +76 -7
  93. data/ext/couchbase/core/impl/query.hxx +36 -0
  94. data/ext/couchbase/core/impl/query_index_manager.cxx +54 -1
  95. data/ext/couchbase/core/impl/replica_utils.cxx +101 -0
  96. data/ext/couchbase/core/impl/replica_utils.hxx +64 -0
  97. data/ext/couchbase/core/impl/resolve_node_id.hxx +125 -0
  98. data/ext/couchbase/core/impl/scope.cxx +70 -0
  99. data/ext/couchbase/core/impl/search.cxx +31 -1
  100. data/ext/couchbase/core/impl/transactions.cxx +130 -0
  101. data/ext/couchbase/core/impl/wait_until_ready.cxx +439 -0
  102. data/ext/couchbase/core/impl/wait_until_ready.hxx +110 -0
  103. data/ext/couchbase/core/impl/with_bucket_config_or_timeout.hxx +180 -0
  104. data/ext/couchbase/core/impl/with_cancellation.hxx +11 -3
  105. data/ext/couchbase/core/io/configuration_belongs_to_session.hxx +1 -2
  106. data/ext/couchbase/core/io/dns_client.cxx +122 -45
  107. data/ext/couchbase/core/io/dns_codec.hxx +21 -21
  108. data/ext/couchbase/core/io/dns_config.cxx +21 -6
  109. data/ext/couchbase/core/io/http_command.hxx +149 -121
  110. data/ext/couchbase/core/io/http_error.hxx +107 -0
  111. data/ext/couchbase/core/io/http_parser.cxx +4 -0
  112. data/ext/couchbase/core/io/http_session.cxx +606 -154
  113. data/ext/couchbase/core/io/http_session.hxx +147 -30
  114. data/ext/couchbase/core/io/http_session_manager.hxx +329 -382
  115. data/ext/couchbase/core/io/http_streaming_parser.cxx +4 -0
  116. data/ext/couchbase/core/io/http_streaming_response.cxx +328 -43
  117. data/ext/couchbase/core/io/http_streaming_response.hxx +37 -3
  118. data/ext/couchbase/core/io/mcbp_buffer_pool.hxx +122 -0
  119. data/ext/couchbase/core/io/mcbp_command.hxx +150 -51
  120. data/ext/couchbase/core/io/mcbp_command_id.hxx +45 -0
  121. data/ext/couchbase/core/io/mcbp_message.cxx +9 -1
  122. data/ext/couchbase/core/io/mcbp_message.hxx +19 -0
  123. data/ext/couchbase/core/io/mcbp_output_queue.hxx +150 -0
  124. data/ext/couchbase/core/io/mcbp_parser.cxx +24 -7
  125. data/ext/couchbase/core/io/mcbp_session.cxx +309 -301
  126. data/ext/couchbase/core/io/mcbp_session.hxx +27 -18
  127. data/ext/couchbase/core/io/opaque_ring_table.hxx +145 -0
  128. data/ext/couchbase/core/io/retry_orchestrator.hxx +6 -5
  129. data/ext/couchbase/core/io/stream_deadline.hxx +50 -0
  130. data/ext/couchbase/core/io/streams.cxx +151 -21
  131. data/ext/couchbase/core/io/streams.hxx +48 -1
  132. data/ext/couchbase/core/logger/logger.cxx +36 -0
  133. data/ext/couchbase/core/logger/logger.hxx +46 -6
  134. data/ext/couchbase/core/logger/redaction.hxx +503 -0
  135. data/ext/couchbase/core/management/eventing_status.hxx +10 -0
  136. data/ext/couchbase/core/management/eventing_status_json.hxx +4 -0
  137. data/ext/couchbase/core/mcbp/big_endian.cxx +1 -1
  138. data/ext/couchbase/core/mcbp/codec.cxx +27 -18
  139. data/ext/couchbase/core/mcbp/operation_queue.cxx +2 -2
  140. data/ext/couchbase/core/mcbp/packet.hxx +2 -2
  141. data/ext/couchbase/core/mcbp/queue_request.cxx +23 -6
  142. data/ext/couchbase/core/mcbp/queue_request.hxx +21 -0
  143. data/ext/couchbase/core/meta/features.hxx +13 -0
  144. data/ext/couchbase/core/meta/version.cxx +6 -3
  145. data/ext/couchbase/core/metrics/logging_meter.cxx +5 -10
  146. data/ext/couchbase/core/metrics/meter_wrapper.cxx +63 -33
  147. data/ext/couchbase/core/metrics/meter_wrapper.hxx +55 -3
  148. data/ext/couchbase/core/operations/analytics_response_parsing.cxx +177 -0
  149. data/ext/couchbase/core/operations/analytics_response_parsing.hxx +55 -0
  150. data/ext/couchbase/core/operations/document_analytics.cxx +48 -130
  151. data/ext/couchbase/core/operations/document_analytics.hxx +14 -2
  152. data/ext/couchbase/core/operations/document_get_all_replicas.hxx +15 -7
  153. data/ext/couchbase/core/operations/document_get_any_replica.hxx +19 -8
  154. data/ext/couchbase/core/operations/document_get_projected.cxx +1 -1
  155. data/ext/couchbase/core/{impl/get_replica.cxx → operations/document_get_replica.cxx} +21 -3
  156. data/ext/couchbase/core/{impl/get_replica.hxx → operations/document_get_replica.hxx} +31 -4
  157. data/ext/couchbase/core/operations/document_lookup_in.cxx +10 -10
  158. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +228 -198
  159. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +215 -217
  160. data/ext/couchbase/core/operations/document_query.cxx +108 -221
  161. data/ext/couchbase/core/operations/document_query.hxx +18 -4
  162. data/ext/couchbase/core/operations/document_search.cxx +52 -8
  163. data/ext/couchbase/core/operations/document_search.hxx +3 -0
  164. data/ext/couchbase/core/operations/document_view.cxx +3 -3
  165. data/ext/couchbase/core/operations/management/analytics_dataset_create.cxx +15 -8
  166. data/ext/couchbase/core/operations/management/analytics_dataset_create.hxx +4 -0
  167. data/ext/couchbase/core/operations/management/analytics_dataset_drop.cxx +11 -6
  168. data/ext/couchbase/core/operations/management/analytics_dataset_get_all.cxx +3 -2
  169. data/ext/couchbase/core/operations/management/analytics_dataverse_create.cxx +10 -5
  170. data/ext/couchbase/core/operations/management/analytics_dataverse_drop.cxx +10 -5
  171. data/ext/couchbase/core/operations/management/analytics_get_pending_mutations.cxx +2 -1
  172. data/ext/couchbase/core/operations/management/analytics_index_create.cxx +33 -8
  173. data/ext/couchbase/core/operations/management/analytics_index_drop.cxx +12 -7
  174. data/ext/couchbase/core/operations/management/analytics_index_get_all.cxx +3 -2
  175. data/ext/couchbase/core/operations/management/analytics_link_connect.cxx +11 -6
  176. data/ext/couchbase/core/operations/management/analytics_link_create.cxx +3 -3
  177. data/ext/couchbase/core/operations/management/analytics_link_disconnect.cxx +10 -4
  178. data/ext/couchbase/core/operations/management/analytics_link_drop.cxx +6 -4
  179. data/ext/couchbase/core/operations/management/analytics_link_get_all.cxx +4 -3
  180. data/ext/couchbase/core/operations/management/analytics_link_replace.cxx +3 -3
  181. data/ext/couchbase/core/operations/management/analytics_link_utils.hxx +1 -1
  182. data/ext/couchbase/core/operations/management/change_password.cxx +2 -0
  183. data/ext/couchbase/core/operations/management/collection_drop.cxx +5 -1
  184. data/ext/couchbase/core/operations/management/error_utils.cxx +59 -5
  185. data/ext/couchbase/core/operations/management/error_utils.hxx +29 -1
  186. data/ext/couchbase/core/operations/management/eventing_deploy_function.cxx +9 -3
  187. data/ext/couchbase/core/operations/management/eventing_drop_function.cxx +8 -3
  188. data/ext/couchbase/core/operations/management/eventing_get_all_functions.cxx +4 -2
  189. data/ext/couchbase/core/operations/management/eventing_get_function.cxx +5 -3
  190. data/ext/couchbase/core/operations/management/eventing_get_status.cxx +4 -2
  191. data/ext/couchbase/core/operations/management/eventing_pause_function.cxx +9 -3
  192. data/ext/couchbase/core/operations/management/eventing_resume_function.cxx +9 -3
  193. data/ext/couchbase/core/operations/management/eventing_undeploy_function.cxx +9 -3
  194. data/ext/couchbase/core/operations/management/eventing_upsert_function.cxx +9 -3
  195. data/ext/couchbase/core/operations/management/group_drop.cxx +3 -1
  196. data/ext/couchbase/core/operations/management/group_get.cxx +3 -1
  197. data/ext/couchbase/core/operations/management/group_upsert.cxx +11 -8
  198. data/ext/couchbase/core/operations/management/query_index_build.cxx +5 -2
  199. data/ext/couchbase/core/operations/management/query_index_create.cxx +9 -3
  200. data/ext/couchbase/core/operations/management/query_index_drop.cxx +11 -4
  201. data/ext/couchbase/core/operations/management/query_index_get_all.cxx +1 -1
  202. data/ext/couchbase/core/operations/management/query_index_get_all_deferred.cxx +1 -1
  203. data/ext/couchbase/core/operations/management/scope_drop.cxx +5 -1
  204. data/ext/couchbase/core/operations/management/scope_get_all.cxx +6 -1
  205. data/ext/couchbase/core/operations/management/search_get_stats.cxx +10 -0
  206. data/ext/couchbase/core/operations/management/search_index_analyze_document.cxx +3 -2
  207. data/ext/couchbase/core/operations/management/search_index_control_ingest.cxx +4 -3
  208. data/ext/couchbase/core/operations/management/search_index_control_plan_freeze.cxx +4 -3
  209. data/ext/couchbase/core/operations/management/search_index_control_query.cxx +4 -3
  210. data/ext/couchbase/core/operations/management/search_index_drop.cxx +2 -2
  211. data/ext/couchbase/core/operations/management/search_index_get.cxx +2 -2
  212. data/ext/couchbase/core/operations/management/search_index_get_documents_count.cxx +3 -2
  213. data/ext/couchbase/core/operations/management/search_index_get_stats.cxx +3 -1
  214. data/ext/couchbase/core/operations/management/search_index_upsert.cxx +2 -2
  215. data/ext/couchbase/core/operations/management/user_drop.cxx +3 -1
  216. data/ext/couchbase/core/operations/management/user_get.cxx +3 -1
  217. data/ext/couchbase/core/operations/management/user_upsert.cxx +12 -9
  218. data/ext/couchbase/core/operations/management/view_index_drop.cxx +1 -1
  219. data/ext/couchbase/core/operations/management/view_index_get.cxx +1 -1
  220. data/ext/couchbase/core/operations/management/view_index_upsert.cxx +1 -1
  221. data/ext/couchbase/core/operations/operation_traits.hxx +18 -0
  222. data/ext/couchbase/core/operations/query_response_parsing.cxx +211 -0
  223. data/ext/couchbase/core/operations/query_response_parsing.hxx +60 -0
  224. data/ext/couchbase/core/operations_fwd.hxx +3 -3
  225. data/ext/couchbase/core/origin.cxx +54 -26
  226. data/ext/couchbase/core/origin.hxx +12 -0
  227. data/ext/couchbase/core/orphan_reporter.cxx +2 -1
  228. data/ext/couchbase/core/platform/random.cc +7 -7
  229. data/ext/couchbase/core/protocol/client_request.cxx +2 -2
  230. data/ext/couchbase/core/protocol/client_request.hxx +38 -21
  231. data/ext/couchbase/core/protocol/client_response.cxx +11 -3
  232. data/ext/couchbase/core/protocol/client_response.hxx +27 -6
  233. data/ext/couchbase/core/protocol/cmd_append.cxx +2 -2
  234. data/ext/couchbase/core/protocol/cmd_cluster_map_change_notification.cxx +2 -2
  235. data/ext/couchbase/core/protocol/cmd_decrement.cxx +6 -6
  236. data/ext/couchbase/core/protocol/cmd_get.cxx +1 -1
  237. data/ext/couchbase/core/protocol/cmd_get_and_lock.cxx +2 -2
  238. data/ext/couchbase/core/protocol/cmd_get_and_touch.cxx +2 -2
  239. data/ext/couchbase/core/protocol/cmd_get_cluster_config.cxx +6 -4
  240. data/ext/couchbase/core/protocol/cmd_get_collection_id.cxx +2 -2
  241. data/ext/couchbase/core/protocol/cmd_get_error_map.cxx +1 -1
  242. data/ext/couchbase/core/protocol/cmd_get_meta.cxx +4 -4
  243. data/ext/couchbase/core/protocol/cmd_get_replica.cxx +1 -1
  244. data/ext/couchbase/core/protocol/cmd_hello.cxx +1 -1
  245. data/ext/couchbase/core/protocol/cmd_increment.cxx +6 -6
  246. data/ext/couchbase/core/protocol/cmd_insert.cxx +4 -4
  247. data/ext/couchbase/core/protocol/cmd_lookup_in.cxx +4 -3
  248. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +4 -3
  249. data/ext/couchbase/core/protocol/cmd_mutate_in.cxx +10 -9
  250. data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +10 -10
  251. data/ext/couchbase/core/protocol/cmd_observe_seqno.cxx +7 -7
  252. data/ext/couchbase/core/protocol/cmd_prepend.cxx +2 -2
  253. data/ext/couchbase/core/protocol/cmd_remove.cxx +2 -2
  254. data/ext/couchbase/core/protocol/cmd_replace.cxx +4 -4
  255. data/ext/couchbase/core/protocol/cmd_touch.cxx +1 -1
  256. data/ext/couchbase/core/protocol/cmd_upsert.cxx +4 -4
  257. data/ext/couchbase/core/protocol/datatype.hxx +118 -1
  258. data/ext/couchbase/core/protocol/frame_info_utils.cxx +1 -1
  259. data/ext/couchbase/core/protocol/magic.hxx +4 -0
  260. data/ext/couchbase/core/protocol/magic_fmt.hxx +3 -0
  261. data/ext/couchbase/core/protocol/server_request.hxx +3 -1
  262. data/ext/couchbase/core/protocol/status.cxx +1 -1
  263. data/ext/couchbase/core/protostellar/analytics_converter.hxx +171 -0
  264. data/ext/couchbase/core/protostellar/bucket_admin_converter.hxx +301 -0
  265. data/ext/couchbase/core/protostellar/collection_admin_converter.hxx +92 -0
  266. data/ext/couchbase/core/protostellar/component.cxx +2445 -0
  267. data/ext/couchbase/core/protostellar/component.hxx +391 -0
  268. data/ext/couchbase/core/{columnar/bootstrap_notification_subscriber.hxx → protostellar/compression_settings.hxx} +11 -10
  269. data/ext/couchbase/core/protostellar/credentials.cxx +233 -0
  270. data/ext/couchbase/core/protostellar/credentials.hxx +88 -0
  271. data/ext/couchbase/core/protostellar/dispatcher.cxx +57 -0
  272. data/ext/couchbase/core/protostellar/dispatcher.hxx +482 -0
  273. data/ext/couchbase/core/protostellar/error_utils.cxx +293 -0
  274. data/ext/couchbase/core/protostellar/error_utils.hxx +97 -0
  275. data/ext/couchbase/core/protostellar/json_payload.hxx +49 -0
  276. data/ext/couchbase/core/protostellar/kv_converter.cxx +70 -0
  277. data/ext/couchbase/core/protostellar/kv_converter.hxx +600 -0
  278. data/ext/couchbase/core/protostellar/query_converter.hxx +265 -0
  279. data/ext/couchbase/core/protostellar/query_index_admin_converter.hxx +291 -0
  280. data/ext/couchbase/core/protostellar/query_proto.hxx +73 -0
  281. data/ext/couchbase/core/protostellar/search_converter.hxx +244 -0
  282. data/ext/couchbase/core/protostellar/search_index_admin_converter.hxx +156 -0
  283. data/ext/couchbase/core/protostellar/view_converter.hxx +158 -0
  284. data/ext/couchbase/core/query_stream.cxx +447 -0
  285. data/ext/couchbase/core/query_stream.hxx +141 -0
  286. data/ext/couchbase/core/query_stream_component.cxx +243 -0
  287. data/ext/couchbase/core/query_stream_component.hxx +68 -0
  288. data/ext/couchbase/core/range_scan_load_balancer.cxx +15 -8
  289. data/ext/couchbase/core/range_scan_orchestrator.cxx +15 -3
  290. data/ext/couchbase/core/response_handler.hxx +41 -4
  291. data/ext/couchbase/core/row_streamer.cxx +332 -46
  292. data/ext/couchbase/core/row_streamer.hxx +31 -1
  293. data/ext/couchbase/core/row_streamer_options.hxx +55 -0
  294. data/ext/couchbase/core/sasl/client.h +3 -0
  295. data/ext/couchbase/core/search_scoring.hxx +66 -0
  296. data/ext/couchbase/core/stream_error_details.hxx +69 -0
  297. data/ext/couchbase/core/timeout_defaults.hxx +0 -1
  298. data/ext/couchbase/core/topology/capabilities.hxx +6 -0
  299. data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
  300. data/ext/couchbase/core/topology/configuration.cxx +21 -6
  301. data/ext/couchbase/core/topology/configuration.hxx +29 -8
  302. data/ext/couchbase/core/topology/configuration_json.hxx +3 -0
  303. data/ext/couchbase/core/topology/error_map_json.hxx +3 -1
  304. data/ext/couchbase/core/tracing/threshold_logging_tracer.cxx +70 -4
  305. data/ext/couchbase/core/tracing/threshold_logging_tracer.hxx +10 -0
  306. data/ext/couchbase/core/transactions/active_transaction_record.cxx +0 -34
  307. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +108 -63
  308. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +19 -2
  309. data/ext/couchbase/core/transactions/error_list.hxx +2 -2
  310. data/ext/couchbase/core/transactions/get_multi_fetch.hxx +160 -0
  311. data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +326 -125
  312. data/ext/couchbase/core/transactions/get_multi_transaction_id.hxx +62 -0
  313. data/ext/couchbase/core/transactions/internal/atr_entry.hxx +22 -2
  314. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +2 -2
  315. data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +23 -1
  316. data/ext/couchbase/core/transactions/internal/utils.hxx +50 -14
  317. data/ext/couchbase/core/transactions/staged_mutation.cxx +23 -18
  318. data/ext/couchbase/core/transactions/staged_mutation.hxx +15 -4
  319. data/ext/couchbase/core/transactions/transaction_context.cxx +55 -40
  320. data/ext/couchbase/core/transactions/transaction_get_result.hxx +16 -0
  321. data/ext/couchbase/core/transactions/transactions.cxx +3 -55
  322. data/ext/couchbase/core/transactions/transactions_cleanup.cxx +165 -72
  323. data/ext/couchbase/core/transactions/transactions_config.cxx +0 -11
  324. data/ext/couchbase/core/transactions/utils.cxx +10 -0
  325. data/ext/couchbase/core/transactions/waitable_op_list.hxx +2 -2
  326. data/ext/couchbase/core/transactions.hxx +9 -9
  327. data/ext/couchbase/core/utils/byteswap.hxx +166 -9
  328. data/ext/couchbase/core/utils/connection_string.cxx +50 -85
  329. data/ext/couchbase/core/utils/connection_string.hxx +15 -0
  330. data/ext/couchbase/core/{columnar/backoff_calculator.hxx → utils/deadline.hxx} +13 -13
  331. data/ext/couchbase/core/utils/duration_parser.cxx +1 -1
  332. data/ext/couchbase/core/utils/json_streaming_lexer.cxx +48 -5
  333. data/ext/couchbase/core/utils/json_streaming_lexer.hxx +4 -1
  334. data/ext/couchbase/core/utils/movable_function.hxx +301 -52
  335. data/ext/couchbase/core/utils/name_codec.hxx +154 -11
  336. data/ext/couchbase/core/{columnar/retry_info.hxx → utils/process_id.hxx} +18 -13
  337. data/ext/couchbase/core/utils/url_codec.cxx +12 -51
  338. data/ext/couchbase/core/websocket_codec.cxx +3 -29
  339. data/ext/couchbase/couchbase/analytics_options.hxx +21 -10
  340. data/ext/couchbase/couchbase/analytics_row.hxx +105 -0
  341. data/ext/couchbase/couchbase/analytics_stream_result.hxx +266 -0
  342. data/ext/couchbase/couchbase/boolean_query.hxx +3 -3
  343. data/ext/couchbase/couchbase/bucket.hxx +57 -0
  344. data/ext/couchbase/couchbase/cluster.hxx +183 -5
  345. data/ext/couchbase/{core/columnar/security_options.hxx → couchbase/cluster_state.hxx} +18 -14
  346. data/ext/couchbase/couchbase/codec/lenient_json_transcoder.hxx +69 -0
  347. data/ext/couchbase/couchbase/collection.hxx +234 -14
  348. data/ext/couchbase/couchbase/common_durability_options.hxx +18 -3
  349. data/ext/couchbase/couchbase/conjunction_query.hxx +1 -1
  350. data/ext/couchbase/couchbase/create_dataset_analytics_options.hxx +6 -1
  351. data/ext/couchbase/couchbase/date_range_query.hxx +2 -2
  352. data/ext/couchbase/couchbase/decrement_options.hxx +1 -1
  353. data/ext/couchbase/couchbase/disjunction_query.hxx +1 -1
  354. data/ext/couchbase/couchbase/error.hxx +32 -0
  355. data/ext/couchbase/couchbase/error_codes.hxx +32 -0
  356. data/ext/couchbase/couchbase/geo_bounding_box_query.hxx +1 -1
  357. data/ext/couchbase/couchbase/geo_distance_query.hxx +1 -1
  358. data/ext/couchbase/couchbase/geo_polygon_query.hxx +1 -1
  359. data/ext/couchbase/couchbase/get_replica_options.hxx +68 -0
  360. data/ext/couchbase/couchbase/get_replica_result.hxx +5 -3
  361. data/ext/couchbase/couchbase/get_replica_strategy.hxx +161 -0
  362. data/ext/couchbase/couchbase/get_result.hxx +1 -0
  363. data/ext/couchbase/couchbase/increment_options.hxx +1 -1
  364. data/ext/couchbase/couchbase/insert_options.hxx +1 -1
  365. data/ext/couchbase/couchbase/match_phrase_query.hxx +1 -1
  366. data/ext/couchbase/couchbase/match_query.hxx +1 -1
  367. data/ext/couchbase/couchbase/mutate_in_options.hxx +3 -3
  368. data/ext/couchbase/couchbase/network_options.hxx +1 -1
  369. data/ext/couchbase/couchbase/node_id.hxx +42 -14
  370. data/ext/couchbase/couchbase/node_id_for_options.hxx +7 -1
  371. data/ext/couchbase/couchbase/node_ids_options.hxx +8 -2
  372. data/ext/couchbase/couchbase/numeric_range_query.hxx +1 -1
  373. data/ext/couchbase/couchbase/phrase_query.hxx +1 -1
  374. data/ext/couchbase/couchbase/prefix_query.hxx +1 -1
  375. data/ext/couchbase/couchbase/query_options.hxx +23 -12
  376. data/ext/couchbase/couchbase/query_row.hxx +111 -0
  377. data/ext/couchbase/couchbase/query_stream_result.hxx +302 -0
  378. data/ext/couchbase/couchbase/query_string_query.hxx +3 -3
  379. data/ext/couchbase/couchbase/regexp_query.hxx +1 -1
  380. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  381. data/ext/couchbase/couchbase/result.hxx +14 -3
  382. data/ext/couchbase/couchbase/scan_result_item.hxx +1 -0
  383. data/ext/couchbase/couchbase/scope.hxx +85 -2
  384. data/ext/couchbase/couchbase/search_options.hxx +58 -8
  385. data/ext/couchbase/couchbase/search_scoring.hxx +94 -0
  386. data/ext/couchbase/couchbase/search_scoring_none.hxx +51 -0
  387. data/ext/couchbase/couchbase/search_scoring_reciprocal_rank_fusion.hxx +88 -0
  388. data/ext/couchbase/couchbase/search_scoring_relative_score_fusion.hxx +70 -0
  389. data/ext/couchbase/couchbase/term_range_query.hxx +1 -1
  390. data/ext/couchbase/couchbase/tracing/request_span.hxx +46 -0
  391. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +23 -21
  392. data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +24 -19
  393. data/ext/couchbase/couchbase/transactions/transaction_get_result.hxx +17 -3
  394. data/ext/couchbase/couchbase/transactions/transactions_config.hxx +4 -2
  395. data/ext/couchbase/couchbase/transactions.hxx +27 -18
  396. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  397. data/ext/couchbase/couchbase/vector_query.hxx +1 -1
  398. data/ext/couchbase/couchbase/wait_until_ready_options.hxx +111 -0
  399. data/ext/couchbase/couchbase/wildcard_query.hxx +1 -1
  400. data/ext/extconf.rb +15 -10
  401. data/ext/rcb_analytics.cxx +82 -102
  402. data/ext/rcb_backend.cxx +42 -36
  403. data/ext/rcb_backend.hxx +2 -0
  404. data/ext/rcb_buckets.cxx +47 -59
  405. data/ext/rcb_collections.cxx +42 -52
  406. data/ext/rcb_crud.cxx +345 -224
  407. data/ext/rcb_diagnostics.cxx +117 -27
  408. data/ext/rcb_exceptions.cxx +152 -99
  409. data/ext/rcb_exceptions.hxx +25 -0
  410. data/ext/rcb_extras.cxx +18 -19
  411. data/ext/rcb_hdr_histogram.cxx +19 -4
  412. data/ext/rcb_logger.cxx +202 -27
  413. data/ext/rcb_logger.hxx +12 -0
  414. data/ext/rcb_multi.cxx +40 -40
  415. data/ext/rcb_observability.cxx +7 -5
  416. data/ext/rcb_query.cxx +99 -142
  417. data/ext/rcb_range_scan.cxx +41 -35
  418. data/ext/rcb_search.cxx +98 -99
  419. data/ext/rcb_users.cxx +36 -56
  420. data/ext/rcb_utils.cxx +100 -37
  421. data/ext/rcb_utils.hxx +287 -33
  422. data/ext/rcb_version.cxx +1 -1
  423. data/ext/rcb_views.cxx +55 -51
  424. data/lib/active_support/cache/couchbase_store.rb +77 -12
  425. data/lib/couchbase/bucket.rb +20 -0
  426. data/lib/couchbase/cluster.rb +17 -0
  427. data/lib/couchbase/collection.rb +36 -1
  428. data/lib/couchbase/errors.rb +18 -0
  429. data/lib/couchbase/fork_hooks.rb +16 -7
  430. data/lib/couchbase/get_replica_strategy.rb +89 -0
  431. data/lib/couchbase/logger.rb +1 -1
  432. data/lib/couchbase/management/analytics_index_manager.rb +15 -15
  433. data/lib/couchbase/management/bucket_manager.rb +6 -6
  434. data/lib/couchbase/management/collection_manager.rb +6 -6
  435. data/lib/couchbase/management/query_index_manager.rb +7 -7
  436. data/lib/couchbase/management/search_index_manager.rb +13 -13
  437. data/lib/couchbase/management/user_manager.rb +10 -10
  438. data/lib/couchbase/management/view_index_manager.rb +5 -5
  439. data/lib/couchbase/options.rb +174 -42
  440. data/lib/couchbase/protostellar/bucket.rb +4 -0
  441. data/lib/couchbase/protostellar/cluster.rb +4 -0
  442. data/lib/couchbase/protostellar/collection.rb +4 -0
  443. data/lib/couchbase/protostellar/error_handling.rb +2 -1
  444. data/lib/couchbase/protostellar/request.rb +1 -0
  445. data/lib/couchbase/protostellar/request_generator/kv.rb +16 -2
  446. data/lib/couchbase/protostellar/request_generator/search.rb +17 -1
  447. data/lib/couchbase/protostellar/retry/orchestrator.rb +3 -2
  448. data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
  449. data/lib/couchbase/protostellar/retry/strategies.rb +1 -0
  450. data/lib/couchbase/search_options.rb +95 -6
  451. data/lib/couchbase/version.rb +1 -1
  452. metadata +91 -33
  453. data/ext/0001-fix-build-for-mingw-w64-ucrt-x86_64-toolchain.patch +0 -40
  454. data/ext/couchbase/core/columnar/agent.hxx +0 -71
  455. data/ext/couchbase/core/columnar/agent_config.hxx +0 -59
  456. data/ext/couchbase/core/columnar/background_bootstrap_listener.hxx +0 -42
  457. data/ext/couchbase/core/columnar/backoff_calculator.cxx +0 -62
  458. data/ext/couchbase/core/columnar/columnar_agent.cxx +0 -147
  459. data/ext/couchbase/core/columnar/columnar_agent_config.cxx +0 -48
  460. data/ext/couchbase/core/columnar/columnar_error.cxx +0 -51
  461. data/ext/couchbase/core/columnar/database_management_options.hxx +0 -56
  462. data/ext/couchbase/core/columnar/error.hxx +0 -51
  463. data/ext/couchbase/core/columnar/error_codes.cxx +0 -107
  464. data/ext/couchbase/core/columnar/error_codes.hxx +0 -75
  465. data/ext/couchbase/core/columnar/management_component.cxx +0 -300
  466. data/ext/couchbase/core/columnar/management_component.hxx +0 -56
  467. data/ext/couchbase/core/columnar/query_component.cxx +0 -523
  468. data/ext/couchbase/core/columnar/query_component.hxx +0 -57
  469. data/ext/couchbase/core/columnar/query_options.hxx +0 -59
  470. data/ext/couchbase/core/columnar/query_result.cxx +0 -130
  471. data/ext/couchbase/core/columnar/query_result.hxx +0 -78
  472. data/ext/couchbase/core/io/config_tracker.cxx +0 -687
  473. data/ext/couchbase/core/io/config_tracker.hxx +0 -123
data/ext/rcb_utils.hxx CHANGED
@@ -24,10 +24,14 @@
24
24
  #include <couchbase/persist_to.hxx>
25
25
  #include <couchbase/read_preference.hxx>
26
26
  #include <couchbase/replicate_to.hxx>
27
+ #include <couchbase/retry_strategy.hxx>
27
28
  #include <couchbase/store_semantics.hxx>
28
29
 
29
30
  #include <chrono>
31
+ #include <cstdarg>
30
32
  #include <cstdint>
33
+ #include <exception>
34
+ #include <memory>
31
35
  #include <optional>
32
36
  #include <string>
33
37
  #include <vector>
@@ -40,6 +44,119 @@
40
44
 
41
45
  namespace couchbase::ruby
42
46
  {
47
+ // Runs fn, which calls Ruby code, under rb_protect. A Ruby non-local exit from fn stops at
48
+ // rb_protect and continues as ruby_jump, so the C++ frames between the caller and cb_method
49
+ // unwind. The longjmp still skips objects held in fn's own frames. fn must not throw; the
50
+ // rb_protect callback is noexcept, so an exception terminates instead of crossing rb_protect's
51
+ // frame.
52
+ template<typename Callable>
53
+ inline void
54
+ cb_protect(const Callable& fn)
55
+ {
56
+ int state = 0;
57
+ rb_protect(
58
+ [](VALUE arg) noexcept -> VALUE {
59
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
60
+ (*reinterpret_cast<const Callable*>(arg))();
61
+ return Qnil;
62
+ },
63
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
64
+ reinterpret_cast<VALUE>(&fn),
65
+ &state);
66
+ if (state != 0) {
67
+ throw ruby_jump(state);
68
+ }
69
+ }
70
+
71
+ // rb_sprintf under cb_protect. A PRIsVALUE argument is formatted with its inspect or to_s, which
72
+ // may raise. Call it only from a method body registered through cb_method.
73
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
74
+ inline auto
75
+ cb_sprintf(const char* format, ...) -> VALUE
76
+ {
77
+ VALUE str = Qnil;
78
+ std::va_list args;
79
+ va_start(args, format);
80
+ try {
81
+ cb_protect([format, &args, &str] {
82
+ str = rb_vsprintf(format, args);
83
+ });
84
+ } catch (...) {
85
+ va_end(args);
86
+ throw;
87
+ }
88
+ va_end(args);
89
+ return str;
90
+ }
91
+
92
+ // NUM2ULL, NUM2LL, NUM2UINT and NUM2INT under cb_protect. Each raises RangeError for a value out of
93
+ // its target range. Call them only from a method body registered through cb_method.
94
+ inline auto
95
+ cb_num2ull(VALUE num) -> std::uint64_t
96
+ {
97
+ std::uint64_t value{};
98
+ cb_protect([num, &value] {
99
+ value = NUM2ULL(num);
100
+ });
101
+ return value;
102
+ }
103
+
104
+ inline auto
105
+ cb_num2ll(VALUE num) -> std::int64_t
106
+ {
107
+ std::int64_t value{};
108
+ cb_protect([num, &value] {
109
+ value = NUM2LL(num);
110
+ });
111
+ return value;
112
+ }
113
+
114
+ inline auto
115
+ cb_num2uint(VALUE num) -> std::uint32_t
116
+ {
117
+ std::uint32_t value{};
118
+ cb_protect([num, &value] {
119
+ value = NUM2UINT(num);
120
+ });
121
+ return value;
122
+ }
123
+
124
+ inline auto
125
+ cb_num2int(VALUE num) -> std::int32_t
126
+ {
127
+ std::int32_t value{};
128
+ cb_protect([num, &value] {
129
+ value = NUM2INT(num);
130
+ });
131
+ return value;
132
+ }
133
+
134
+ // Wraps Method, which may throw ruby_exception or ruby_jump, for rb_define_method. Method's
135
+ // frames unwind first; the Ruby raise or jump happens here, where no C++ object is live and no
136
+ // catch handler is active.
137
+ template<auto Method, typename = decltype(Method)>
138
+ struct cb_method;
139
+
140
+ template<auto Method, typename... Args>
141
+ struct cb_method<Method, VALUE (*)(Args...)> {
142
+ static auto invoke(Args... args) -> VALUE
143
+ {
144
+ int state = 0;
145
+ VALUE exc = Qnil;
146
+ try {
147
+ return Method(args...);
148
+ } catch (const ruby_jump& e) {
149
+ state = e.state();
150
+ } catch (const ruby_exception& e) {
151
+ exc = e.exception_object();
152
+ }
153
+ if (state != 0) {
154
+ rb_jump_tag(state);
155
+ }
156
+ rb_exc_raise(exc);
157
+ }
158
+ };
159
+
43
160
  template<typename Future>
44
161
  inline auto
45
162
  cb_wait_for_future(Future&& f) -> decltype(f.get())
@@ -48,17 +165,84 @@ cb_wait_for_future(Future&& f) -> decltype(f.get())
48
165
  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
49
166
  Future&& f;
50
167
  decltype(f.get()) res{};
51
- } arg{ std::forward<Future>(f) };
52
- rb_thread_call_without_gvl(
53
- [](void* param) -> void* {
168
+ std::exception_ptr error{};
169
+ bool done{ false };
170
+
171
+ // Runs without the GVL, inside Ruby C frames, so no exception leaves it.
172
+ static auto wait(void* param) -> void*
173
+ {
54
174
  auto* pack = static_cast<arg_pack*>(param);
55
- pack->res = std::move(pack->f.get());
175
+ try {
176
+ pack->res = std::move(pack->f.get());
177
+ } catch (...) {
178
+ pack->error = std::current_exception();
179
+ }
180
+ pack->done = true;
56
181
  return nullptr;
57
- },
58
- &arg,
59
- nullptr,
60
- nullptr);
61
- flush_logger();
182
+ }
183
+
184
+ // No unblocking function: the wait ends only when the operation completes or times out.
185
+ // Interrupts are checked before and after it, and a pending one raises there.
186
+ static auto protected_wait(VALUE param) -> VALUE
187
+ {
188
+ rb_thread_call_without_gvl(
189
+ wait,
190
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr)
191
+ reinterpret_cast<void*>(param),
192
+ nullptr,
193
+ nullptr);
194
+ return Qnil;
195
+ }
196
+ } arg{ std::forward<Future>(f) };
197
+
198
+ // rb_protect stops an interrupt (Timeout, Thread#raise, Thread#kill, a signal) raised at the
199
+ // checks around the wait. One raised before the wait leaves the operation in flight, so the
200
+ // wait is retried until the operation completes; no attempt waits with the GVL held. A later
201
+ // interrupt replaces the earlier one, which is discarded.
202
+ int state = 0;
203
+ VALUE interrupt = Qnil;
204
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
205
+ rb_protect(arg_pack::protected_wait, reinterpret_cast<VALUE>(&arg), &state);
206
+ if (state != 0) {
207
+ interrupt = rb_errinfo();
208
+ }
209
+ while (!arg.done) {
210
+ int next = 0;
211
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
212
+ rb_protect(arg_pack::protected_wait, reinterpret_cast<VALUE>(&arg), &next);
213
+ if (next != 0) {
214
+ state = next;
215
+ interrupt = rb_errinfo();
216
+ }
217
+ }
218
+ if (state != 0) {
219
+ // A trap handler run by a retry's interrupt check can clear errinfo by rescuing, so a raised
220
+ // exception is restored before ruby_jump carries the interrupt to cb_method. Trap handlers run
221
+ // only on the main thread, which never receives a kill interrupt. errinfo holds an Exception
222
+ // only after a raise: a kill leaves an Integer, and a throw (Timeout on Ruby 3.2 uses catch
223
+ // and throw) leaves internal throw data, which rb_obj_is_kind_of cannot read.
224
+ if (RB_TYPE_P(interrupt, T_OBJECT) && rb_obj_is_kind_of(interrupt, rb_eException) == Qtrue) {
225
+ rb_set_errinfo(interrupt);
226
+ }
227
+ throw ruby_jump(state);
228
+ }
229
+ // Only when no interrupt was stopped: the logger runs Ruby code, which would replace errinfo.
230
+ // Otherwise its messages stay queued for the next flush.
231
+ cb_protect([] {
232
+ flush_logger();
233
+ });
234
+ if (arg.error) {
235
+ // The promises waited on here are fulfilled only with set_value, so a future fails only with
236
+ // std::future_error, when its promise is destroyed unfulfilled.
237
+ std::string message{ "unable to wait for the operation result" };
238
+ try {
239
+ std::rethrow_exception(arg.error);
240
+ } catch (const std::exception& e) {
241
+ message.append(": ").append(e.what());
242
+ } catch (...) {
243
+ }
244
+ throw ruby_exception(cb_exc_new(exc_couchbase_error(), message));
245
+ }
62
246
  return std::move(arg.res);
63
247
  }
64
248
 
@@ -72,6 +256,15 @@ cb_str_new(const StringLike str)
72
256
  void
73
257
  cb_check_type(VALUE object, int type);
74
258
 
259
+ // rb_sym2id for a value not yet checked to be a Symbol. A non-Symbol throws the TypeError, with the
260
+ // message, that rb_sym2id raises. Call it only from a method body registered through cb_method.
261
+ inline auto
262
+ cb_sym2id(VALUE sym) -> ID
263
+ {
264
+ cb_check_type(sym, T_SYMBOL);
265
+ return rb_sym2id(sym);
266
+ }
267
+
75
268
  std::string
76
269
  cb_string_new(VALUE str);
77
270
 
@@ -124,11 +317,11 @@ cb_extract_timeout(Request& req, VALUE options)
124
317
  return cb_extract_timeout(req, rb_hash_aref(options, rb_id2sym(rb_intern("timeout"))));
125
318
  case T_FIXNUM:
126
319
  case T_BIGNUM:
127
- req.timeout = std::chrono::milliseconds(NUM2ULL(options));
320
+ req.timeout = std::chrono::milliseconds(cb_num2ull(options));
128
321
  break;
129
322
  default:
130
323
  throw ruby_exception(
131
- rb_eArgError, rb_sprintf("timeout must be an Integer, but given %+" PRIsVALUE, options));
324
+ rb_eArgError, cb_sprintf("timeout must be an Integer, but given %+" PRIsVALUE, options));
132
325
  }
133
326
  }
134
327
  }
@@ -143,7 +336,7 @@ cb_extract_durability_level(Request& req, VALUE options)
143
336
 
144
337
  if (TYPE(options) != T_HASH) {
145
338
  throw ruby_exception(rb_eArgError,
146
- rb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
339
+ cb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
147
340
  }
148
341
 
149
342
  static VALUE property_name = rb_id2sym(rb_intern("durability_level"));
@@ -154,7 +347,7 @@ cb_extract_durability_level(Request& req, VALUE options)
154
347
  }
155
348
  if (TYPE(val) != T_SYMBOL) {
156
349
  throw ruby_exception(
157
- rb_eArgError, rb_sprintf("durability_level must be a Symbol, but given %+" PRIsVALUE, val));
350
+ rb_eArgError, cb_sprintf("durability_level must be a Symbol, but given %+" PRIsVALUE, val));
158
351
  }
159
352
  if (ID level = rb_sym2id(val); level == rb_intern("none")) {
160
353
  req.durability_level = couchbase::durability_level::none;
@@ -166,7 +359,7 @@ cb_extract_durability_level(Request& req, VALUE options)
166
359
  req.durability_level = couchbase::durability_level::persist_to_majority;
167
360
  } else {
168
361
  throw ruby_exception(rb_eArgError,
169
- rb_sprintf("unexpected durability_level, given %+" PRIsVALUE, val));
362
+ cb_sprintf("unexpected durability_level, given %+" PRIsVALUE, val));
170
363
  }
171
364
  }
172
365
 
@@ -178,7 +371,7 @@ cb_extract_read_preference(Request& req, VALUE options)
178
371
  if (!NIL_P(options)) {
179
372
  if (TYPE(options) != T_HASH) {
180
373
  throw ruby_exception(rb_eArgError,
181
- rb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
374
+ cb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
182
375
  }
183
376
 
184
377
  VALUE val = rb_hash_aref(options, property_name);
@@ -187,7 +380,7 @@ cb_extract_read_preference(Request& req, VALUE options)
187
380
  }
188
381
  if (TYPE(val) != T_SYMBOL) {
189
382
  throw ruby_exception(
190
- rb_eArgError, rb_sprintf("read_preference must be a Symbol, but given %+" PRIsVALUE, val));
383
+ rb_eArgError, cb_sprintf("read_preference must be a Symbol, but given %+" PRIsVALUE, val));
191
384
  }
192
385
 
193
386
  if (ID mode = rb_sym2id(val); mode == rb_intern("no_preference")) {
@@ -196,7 +389,7 @@ cb_extract_read_preference(Request& req, VALUE options)
196
389
  req.read_preference = couchbase::read_preference::selected_server_group;
197
390
  } else {
198
391
  throw ruby_exception(rb_eArgError,
199
- rb_sprintf("unexpected read_preference, given %+" PRIsVALUE, val));
392
+ cb_sprintf("unexpected read_preference, given %+" PRIsVALUE, val));
200
393
  }
201
394
  }
202
395
  }
@@ -211,15 +404,29 @@ cb_extract_duration(Field& field, VALUE options, const char* name)
211
404
  return cb_extract_duration(field, rb_hash_aref(options, rb_id2sym(rb_intern(name))), name);
212
405
  case T_FIXNUM:
213
406
  case T_BIGNUM:
214
- field = std::chrono::milliseconds(NUM2ULL(options));
407
+ field = std::chrono::milliseconds(cb_num2ull(options));
215
408
  break;
216
409
  default:
217
410
  throw ruby_exception(
218
- rb_eArgError, rb_sprintf("%s must be an Integer, but given %+" PRIsVALUE, name, options));
411
+ rb_eArgError, cb_sprintf("%s must be an Integer, but given %+" PRIsVALUE, name, options));
219
412
  }
220
413
  }
221
414
  }
222
415
 
416
+ // The default upper bound of cb_to_timeout. It lies far inside the range of steady_clock, so a
417
+ // deadline steady_clock::now() + timeout cannot overflow.
418
+ constexpr std::chrono::milliseconds cb_max_timeout{ std::chrono::hours{ 24 * 365 } };
419
+ static_assert(cb_max_timeout < std::chrono::steady_clock::duration::max() / 2);
420
+
421
+ // Converts timeout, an Integer number of milliseconds from 0 to max_timeout, which must not be
422
+ // negative. Throws ArgumentError for a non-Integer, and Error::InvalidArgument for a value out of
423
+ // that range; name labels the value in the messages. Call it only from a method body registered
424
+ // through cb_method.
425
+ [[nodiscard]] auto
426
+ cb_to_timeout(VALUE timeout,
427
+ const char* name,
428
+ std::chrono::milliseconds max_timeout = cb_max_timeout) -> std::chrono::milliseconds;
429
+
223
430
  void
224
431
  cb_extract_content(std::vector<std::byte>& field, VALUE content);
225
432
 
@@ -246,6 +453,11 @@ cb_extract_timeout(std::chrono::milliseconds& field, VALUE options);
246
453
  void
247
454
  cb_extract_timeout(std::optional<std::chrono::milliseconds>& field, VALUE options);
248
455
 
456
+ // nil selects the cluster's default strategy; :best_effort and :fail_fast select the built-ins.
457
+ // Any other value raises InvalidArgument.
458
+ auto
459
+ cb_extract_retry_strategy(VALUE options) -> std::shared_ptr<couchbase::retry_strategy>;
460
+
249
461
  void
250
462
  cb_extract_option_symbol(VALUE& val, VALUE options, const char* name);
251
463
 
@@ -276,7 +488,7 @@ cb_extract_option_bool(Boolean& field, VALUE options, const char* name)
276
488
  break;
277
489
  default:
278
490
  throw ruby_exception(rb_eArgError,
279
- rb_sprintf("%s must be a Boolean, but given %+" PRIsVALUE, name, val));
491
+ cb_sprintf("%s must be a Boolean, but given %+" PRIsVALUE, name, val));
280
492
  }
281
493
  }
282
494
  }
@@ -291,7 +503,7 @@ cb_extract_option_uint64(T& field, VALUE options, const char* name)
291
503
  VALUE val = Qnil;
292
504
  cb_extract_option_bignum(val, options, name);
293
505
  if (!NIL_P(val)) {
294
- field = NUM2ULL(val);
506
+ field = cb_num2ull(val);
295
507
  }
296
508
  }
297
509
 
@@ -309,11 +521,11 @@ cb_extract_option_number(Integer& field, VALUE options, const char* name)
309
521
  field = static_cast<Integer>(FIX2ULONG(val));
310
522
  break;
311
523
  case T_BIGNUM:
312
- field = static_cast<Integer>(NUM2ULL(val));
524
+ field = static_cast<Integer>(cb_num2ull(val));
313
525
  break;
314
526
  default:
315
527
  throw ruby_exception(rb_eArgError,
316
- rb_sprintf("%s must be a Integer, but given %+" PRIsVALUE, name, val));
528
+ cb_sprintf("%s must be a Integer, but given %+" PRIsVALUE, name, val));
317
529
  }
318
530
  }
319
531
  }
@@ -417,7 +629,7 @@ set_timeout(CommandOptions& opts, VALUE options)
417
629
  if (!NIL_P(options)) {
418
630
  if (TYPE(options) != T_HASH) {
419
631
  throw ruby_exception(rb_eArgError,
420
- rb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
632
+ cb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
421
633
  }
422
634
  VALUE val = rb_hash_aref(options, property_name);
423
635
  if (NIL_P(val)) {
@@ -426,12 +638,12 @@ set_timeout(CommandOptions& opts, VALUE options)
426
638
  switch (TYPE(val)) {
427
639
  case T_FIXNUM:
428
640
  case T_BIGNUM:
429
- opts.timeout(std::chrono::milliseconds(NUM2ULL(val)));
641
+ opts.timeout(std::chrono::milliseconds(cb_num2ull(val)));
430
642
  break;
431
643
 
432
644
  default:
433
645
  throw ruby_exception(rb_eArgError,
434
- rb_sprintf("timeout must be an Integer, but given %+" PRIsVALUE, val));
646
+ cb_sprintf("timeout must be an Integer, but given %+" PRIsVALUE, val));
435
647
  }
436
648
  }
437
649
  }
@@ -453,7 +665,7 @@ set_expiry(CommandOptions& opts, VALUE options)
453
665
  if (!NIL_P(options)) {
454
666
  if (TYPE(options) != T_HASH) {
455
667
  throw ruby_exception(rb_eArgError,
456
- rb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
668
+ cb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
457
669
  }
458
670
  VALUE val = rb_hash_aref(options, property_name);
459
671
  if (NIL_P(val)) {
@@ -483,7 +695,7 @@ set_preserve_expiry(CommandOptions& opts, VALUE options)
483
695
  if (!NIL_P(options)) {
484
696
  if (TYPE(options) != T_HASH) {
485
697
  throw ruby_exception(rb_eArgError,
486
- rb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
698
+ cb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
487
699
  }
488
700
  VALUE val = rb_hash_aref(options, property_name);
489
701
  if (NIL_P(val)) {
@@ -499,7 +711,7 @@ set_preserve_expiry(CommandOptions& opts, VALUE options)
499
711
  default:
500
712
  throw ruby_exception(
501
713
  rb_eArgError,
502
- rb_sprintf("preserve_expiry must be a Boolean, but given %+" PRIsVALUE, val));
714
+ cb_sprintf("preserve_expiry must be a Boolean, but given %+" PRIsVALUE, val));
503
715
  }
504
716
  }
505
717
  }
@@ -517,7 +729,7 @@ set_durability(CommandOptions& opts, VALUE options)
517
729
  if (!NIL_P(options)) {
518
730
  if (TYPE(options) != T_HASH) {
519
731
  throw ruby_exception(rb_eArgError,
520
- rb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
732
+ cb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
521
733
  }
522
734
  if (auto level = extract_durability_level(options); level.has_value()) {
523
735
  opts.durability(level.value());
@@ -541,7 +753,7 @@ cb_extract_store_semantics(Request& req, VALUE options)
541
753
  }
542
754
  if (TYPE(options) != T_HASH) {
543
755
  throw ruby_exception(rb_eArgError,
544
- rb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
756
+ cb_sprintf("expected options to be Hash, given %+" PRIsVALUE, options));
545
757
  }
546
758
 
547
759
  VALUE val = rb_hash_aref(options, property_name);
@@ -550,7 +762,7 @@ cb_extract_store_semantics(Request& req, VALUE options)
550
762
  }
551
763
  if (TYPE(val) != T_SYMBOL) {
552
764
  throw ruby_exception(
553
- rb_eArgError, rb_sprintf("store_semantics must be a Symbol, but given %+" PRIsVALUE, val));
765
+ rb_eArgError, cb_sprintf("store_semantics must be a Symbol, but given %+" PRIsVALUE, val));
554
766
  }
555
767
 
556
768
  if (ID mode = rb_sym2id(val); mode == rb_intern("replace")) {
@@ -561,8 +773,50 @@ cb_extract_store_semantics(Request& req, VALUE options)
561
773
  req.store_semantics = couchbase::store_semantics::upsert;
562
774
  } else {
563
775
  throw ruby_exception(rb_eArgError,
564
- rb_sprintf("unexpected store_semantics, given %+" PRIsVALUE, val));
776
+ cb_sprintf("unexpected store_semantics, given %+" PRIsVALUE, val));
777
+ }
778
+ }
779
+
780
+ // Copies a Hash of String => String into target. The type check does not raise or throw inside
781
+ // rb_hash_foreach: the callback records the first entry of another type and stops, and the
782
+ // ArgumentError is thrown after rb_hash_foreach returns. kind names the option in the message.
783
+ template<typename Map>
784
+ void
785
+ cb_extract_string_hash(Map& target, VALUE hash, const char* kind)
786
+ {
787
+ struct state {
788
+ Map& target;
789
+ VALUE key{ Qundef };
790
+ VALUE value{ Qundef };
791
+ } st{ target };
792
+ rb_hash_foreach(
793
+ hash,
794
+ [](VALUE key, VALUE value, VALUE arg) -> int {
795
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
796
+ auto* s = reinterpret_cast<state*>(arg);
797
+ if (!RB_TYPE_P(key, T_STRING) || !RB_TYPE_P(value, T_STRING)) {
798
+ s->key = key;
799
+ s->value = value;
800
+ return ST_STOP;
801
+ }
802
+ s->target[cb_string_new(key)] = cb_string_new(value);
803
+ return ST_CONTINUE;
804
+ },
805
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
806
+ reinterpret_cast<VALUE>(&st));
807
+ if (st.key == Qundef) {
808
+ return;
809
+ }
810
+ if (!RB_TYPE_P(st.key, T_STRING)) {
811
+ throw ruby_exception(
812
+ rb_eArgError, cb_sprintf("%s key must be a String, but given %+" PRIsVALUE, kind, st.key));
565
813
  }
814
+ throw ruby_exception(rb_eArgError,
815
+ cb_sprintf("%s value for %+" PRIsVALUE
816
+ " must be a String, but given %+" PRIsVALUE,
817
+ kind,
818
+ st.key,
819
+ st.value));
566
820
  }
567
821
  } // namespace couchbase::ruby
568
822
 
data/ext/rcb_version.cxx CHANGED
@@ -120,7 +120,7 @@ init_version(VALUE mCouchbase)
120
120
  name == "mozilla_ca_bundle_size") {
121
121
  rb_hash_aset(cb_CoreInfo, rb_id2sym(rb_intern(name.c_str())), INT2FIX(std::stoi(value)));
122
122
  } else if (name == "snapshot" || name == "static_stdlib" || name == "static_openssl" ||
123
- name == "static_boringssl" || name == "columnar" || name == "static_target" ||
123
+ name == "static_boringssl" || name == "static_target" ||
124
124
  name == "mozilla_ca_bundle_embedded") {
125
125
  rb_hash_aset(
126
126
  cb_CoreInfo, rb_id2sym(rb_intern(name.c_str())), value == "true" ? Qtrue : Qfalse);