couchbase 3.8.2 → 3.9.1

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 +136 -23
  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 +82 -7
  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 +1551 -397
  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 +60 -6
  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 +13 -37
  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 +33 -1
  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
@@ -0,0 +1,447 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2026. Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "query_stream.hxx"
19
+
20
+ #include <couchbase/error_codes.hxx>
21
+
22
+ #include "core/io/http_error.hxx"
23
+ #include "core/operations/query_response_parsing.hxx"
24
+ #include "free_form_http_request.hxx"
25
+ #include "logger/logger.hxx"
26
+ #include "row_streamer.hxx"
27
+ #include "utils/json.hxx"
28
+ #include "utils/movable_function.hxx"
29
+
30
+ #include <asio/io_context.hpp>
31
+ #include <asio/post.hpp>
32
+ #include <tao/json/value.hpp>
33
+
34
+ #include <atomic>
35
+ #include <cstddef>
36
+ #include <memory>
37
+ #include <mutex>
38
+ #include <optional>
39
+ #include <string>
40
+ #include <system_error>
41
+ #include <utility>
42
+ #include <vector>
43
+
44
+ namespace couchbase::core
45
+ {
46
+ namespace
47
+ {
48
+ constexpr auto N1QL_RESULTS_POINTER = "/results/^";
49
+
50
+ // The bounded streaming lexer surfaces malformed- or oversized-JSON failures as
51
+ // errc::streaming_json_lexer::* codes. The buffered query() path normalizes any body-parse failure
52
+ // to errc::common::parsing_failure (document_query.cxx), and so does row_streamer's own
53
+ // truncated-body terminal; normalize the lexer codes here too so the public streaming error
54
+ // contract matches the buffered one (a consumer matching parsing_failure never silently misses a
55
+ // malformed response).
56
+ auto
57
+ normalize_stream_error(std::error_code ec) -> std::error_code
58
+ {
59
+ if (ec.category() == couchbase::core::impl::streaming_json_lexer_category()) {
60
+ return errc::common::parsing_failure;
61
+ }
62
+ return ec;
63
+ }
64
+ } // namespace
65
+
66
+ // Abstract backing for a query_stream. Two implementations: one that lazily pulls rows off a live
67
+ // socket (streaming_query_stream_impl), and one that replays an already-buffered response without
68
+ // re-parsing it (buffered_query_stream_impl), used for the prepared-statement path.
69
+ class query_stream_impl
70
+ {
71
+ public:
72
+ query_stream_impl() = default;
73
+ query_stream_impl(const query_stream_impl&) = delete;
74
+ query_stream_impl(query_stream_impl&&) = delete;
75
+ auto operator=(const query_stream_impl&) -> query_stream_impl& = delete;
76
+ auto operator=(query_stream_impl&&) -> query_stream_impl& = delete;
77
+ virtual ~query_stream_impl() = default;
78
+
79
+ virtual void start(utils::movable_function<void(std::error_code)>&& on_ready) = 0;
80
+ virtual void next_row(
81
+ utils::movable_function<void(std::optional<std::string>, std::error_code)>&& handler) = 0;
82
+ [[nodiscard]] virtual auto signature() const -> std::optional<std::string> = 0;
83
+ [[nodiscard]] virtual auto meta_data() const
84
+ -> std::optional<operations::query_response::query_meta_data> = 0;
85
+ [[nodiscard]] virtual auto error_details() const -> stream_error_details = 0;
86
+ virtual auto set_deadline(std::chrono::time_point<std::chrono::steady_clock> deadline_tp)
87
+ -> io::deadline_state = 0;
88
+ virtual void cancel() = 0;
89
+ };
90
+
91
+ namespace
92
+ {
93
+ // Lift the service-reported diagnostics out of a parsed preamble/trailer. Called for both sections,
94
+ // so whichever one carried the failure is what the error context ends up describing.
95
+ auto
96
+ extract_error_details(const operations::query_response::query_meta_data& meta, std::string raw)
97
+ -> stream_error_details
98
+ {
99
+ stream_error_details details{};
100
+ details.client_context_id = meta.client_context_id;
101
+ // Retained whether or not the section reports an error: it becomes error_context::http_body,
102
+ // which the buffered path fills for every response, and even an error-free envelope names the
103
+ // request the service logged.
104
+ details.raw = std::move(raw);
105
+ if (meta.errors && !meta.errors->empty()) {
106
+ details.first_error_code = meta.errors->front().code;
107
+ details.first_error_message = meta.errors->front().message;
108
+ }
109
+ return details;
110
+ }
111
+ } // namespace
112
+
113
+ namespace
114
+ {
115
+ class streaming_query_stream_impl
116
+ : public query_stream_impl
117
+ , public std::enable_shared_from_this<streaming_query_stream_impl>
118
+ {
119
+ public:
120
+ streaming_query_stream_impl(asio::io_context& io,
121
+ http_response_body body,
122
+ row_streamer_options options,
123
+ std::uint32_t http_status)
124
+ : streamer_{ io, std::move(body), N1QL_RESULTS_POINTER, options }
125
+ , http_status_{ http_status }
126
+ {
127
+ }
128
+ streaming_query_stream_impl(const streaming_query_stream_impl&) = delete;
129
+ streaming_query_stream_impl(streaming_query_stream_impl&&) = delete;
130
+ auto operator=(const streaming_query_stream_impl&) -> streaming_query_stream_impl& = delete;
131
+ auto operator=(streaming_query_stream_impl&&) -> streaming_query_stream_impl& = delete;
132
+
133
+ // Runs with no handle left, no pull in flight and the preamble delivered: each pull, and start()
134
+ // until the preamble, holds this object.
135
+ //
136
+ // Cancels unless a pull delivered the terminal of a completed JSON document. Without the cancel,
137
+ // the read-ahead keeps the body and its session alive:
138
+ // - through a read in flight, until the idle timer fires or the cluster closes;
139
+ // - through a row send queued on a full channel, indefinitely.
140
+ // The cancel stops the connection only while the body is still being read. A body read to its
141
+ // end has already checked the connection in.
142
+ //
143
+ // After a completed document the read-ahead reads the trailing bytes and the connection is
144
+ // checked in, or the idle timer ends the body.
145
+ ~streaming_query_stream_impl() override
146
+ {
147
+ if (!document_completed_) {
148
+ streamer_.cancel();
149
+ }
150
+ }
151
+
152
+ void start(utils::movable_function<void(std::error_code)>&& on_ready) override
153
+ {
154
+ streamer_.start([self = shared_from_this(), on_ready = std::move(on_ready)](
155
+ std::string preamble, std::error_code ec) mutable {
156
+ if (ec) {
157
+ return on_ready(normalize_stream_error(
158
+ io::invalid_argument_for_undecodable_400(ec, self->http_status_, {})));
159
+ }
160
+ operations::query_response::query_meta_data meta{};
161
+ try {
162
+ meta = operations::parse_query_meta(utils::json::parse(preamble));
163
+ } catch (const std::exception&) {
164
+ {
165
+ // Keep the unparseable preamble: it is the only diagnostic there is for a malformed
166
+ // response, and the buffered path reports the raw body in the same situation.
167
+ const std::scoped_lock lock{ self->mutex_ };
168
+ self->error_details_.raw = std::move(preamble);
169
+ }
170
+ return on_ready(io::invalid_argument_for_undecodable_400(
171
+ errc::common::parsing_failure, self->http_status_, {}));
172
+ }
173
+ {
174
+ const std::scoped_lock lock{ self->mutex_ };
175
+ self->signature_ = meta.signature;
176
+ self->error_details_ = extract_error_details(meta, std::move(preamble));
177
+ }
178
+ // An upfront error is rare for the streaming path (status/errors usually arrive in the
179
+ // trailer). Mirror the analytics preamble check and gate on error entries rather than status:
180
+ // a benign non-success preamble status (e.g. "running") would otherwise be handed to
181
+ // map_query_error, whose no-errors fallback reports an error — a spurious
182
+ // error. Checking errors also surfaces a genuine preamble error now rather than one next()
183
+ // call later.
184
+ std::error_code early_error{};
185
+ if (meta.errors && !meta.errors->empty()) {
186
+ early_error = operations::map_query_error(meta, self->http_status_);
187
+ }
188
+ on_ready(early_error);
189
+ });
190
+ }
191
+
192
+ void next_row(
193
+ utils::movable_function<void(std::optional<std::string>, std::error_code)>&& handler) override
194
+ {
195
+ // Terminal-sticky: once the stream has ended, re-deliver the stored terminal on every
196
+ // subsequent pull instead of issuing another receive that would park forever on the
197
+ // drained-but-open row channel. The public handle guards this too, but a core::-direct consumer
198
+ // (e.g. a wrapper) relies on this being safe.
199
+ std::optional<std::error_code> sticky_terminal;
200
+ {
201
+ const std::scoped_lock lock{ mutex_ };
202
+ if (terminal_reached_) {
203
+ sticky_terminal = terminal_ec_;
204
+ }
205
+ }
206
+ if (sticky_terminal) {
207
+ // Deliver outside the lock: the handler typically issues the next pull, which re-enters
208
+ // next_row and would re-lock the non-recursive mutex_ and deadlock if invoked while held.
209
+ return handler(std::nullopt, *sticky_terminal);
210
+ }
211
+ streamer_.next_row([self = shared_from_this(),
212
+ handler = std::move(handler)](std::string row, std::error_code ec) mutable {
213
+ if (!row.empty()) {
214
+ return handler(std::move(row), {});
215
+ }
216
+ // Empty row marks the end of the stream. Classify the terminal, remember it (so later pulls
217
+ // are served the sticky terminal above), then deliver.
218
+ std::error_code terminal_ec{};
219
+ if (ec) {
220
+ // Normalize a mid-document lexer failure to parsing_failure, matching the buffered path.
221
+ terminal_ec = normalize_stream_error(
222
+ io::invalid_argument_for_undecodable_400(ec, self->http_status_, {}));
223
+ } else if (auto raw_meta = self->streamer_.metadata(); !raw_meta.has_value()) {
224
+ // A clean end without reconstructed metadata violates the row_streamer invariant; surface
225
+ // it rather than silently reporting success with no metadata.
226
+ CB_LOG_WARNING("query_stream: end reached but row_streamer returned no metadata");
227
+ terminal_ec = errc::common::internal_server_failure;
228
+ } else {
229
+ try {
230
+ auto meta = operations::parse_query_meta(utils::json::parse(raw_meta.value()));
231
+ terminal_ec = operations::map_query_error(meta, self->http_status_);
232
+ const std::scoped_lock lock{ self->mutex_ };
233
+ // The trailer is where a request that failed part-way reports itself, so its diagnostics
234
+ // supersede the preamble's for the terminal error.
235
+ self->error_details_ = extract_error_details(meta, std::move(raw_meta.value()));
236
+ self->meta_data_ = std::move(meta);
237
+ } catch (const std::exception&) {
238
+ terminal_ec = io::invalid_argument_for_undecodable_400(
239
+ errc::common::parsing_failure, self->http_status_, {});
240
+ const std::scoped_lock lock{ self->mutex_ };
241
+ self->error_details_.raw = std::move(raw_meta.value());
242
+ }
243
+ }
244
+ {
245
+ const std::scoped_lock lock{ self->mutex_ };
246
+ self->terminal_reached_ = true;
247
+ self->terminal_ec_ = terminal_ec;
248
+ self->document_completed_ = !ec;
249
+ }
250
+ handler(std::nullopt, terminal_ec);
251
+ });
252
+ }
253
+
254
+ [[nodiscard]] auto signature() const -> std::optional<std::string> override
255
+ {
256
+ const std::scoped_lock lock{ mutex_ };
257
+ return signature_;
258
+ }
259
+
260
+ [[nodiscard]] auto meta_data() const
261
+ -> std::optional<operations::query_response::query_meta_data> override
262
+ {
263
+ const std::scoped_lock lock{ mutex_ };
264
+ return meta_data_;
265
+ }
266
+
267
+ [[nodiscard]] auto error_details() const -> stream_error_details override
268
+ {
269
+ const std::scoped_lock lock{ mutex_ };
270
+ return error_details_;
271
+ }
272
+
273
+ auto set_deadline(std::chrono::time_point<std::chrono::steady_clock> deadline_tp)
274
+ -> io::deadline_state override
275
+ {
276
+ return streamer_.set_deadline(deadline_tp);
277
+ }
278
+
279
+ void cancel() override
280
+ {
281
+ streamer_.cancel();
282
+ }
283
+
284
+ private:
285
+ row_streamer streamer_;
286
+ std::uint32_t http_status_{};
287
+ mutable std::mutex mutex_{};
288
+ std::optional<std::string> signature_{};
289
+ std::optional<operations::query_response::query_meta_data> meta_data_{};
290
+ stream_error_details error_details_{};
291
+ // Terminal-sticky state (guarded by mutex_): once next_row reports the terminal, it is remembered
292
+ // so every later pull re-delivers it instead of parking on the drained channel.
293
+ bool terminal_reached_{ false };
294
+ std::error_code terminal_ec_{};
295
+ // Set when a pull delivers a terminal with no streamer error: the lexer completed the JSON
296
+ // document. Written under mutex_. The destructor reads it unlocked: the release of the last
297
+ // shared_ptr orders that read after every write.
298
+ bool document_completed_{ false };
299
+ };
300
+
301
+ // Replays an already-buffered query_response as a stream without any JSON re-parsing: the rows are
302
+ // already row-JSON strings and the metadata is already parsed. Used for the prepared-statement
303
+ // (adhoc == false) path, which must run buffered but presents the uniform streaming handle.
304
+ class buffered_query_stream_impl
305
+ : public query_stream_impl
306
+ , public std::enable_shared_from_this<buffered_query_stream_impl>
307
+ {
308
+ public:
309
+ buffered_query_stream_impl(asio::io_context& io,
310
+ std::vector<std::string> rows,
311
+ operations::query_response::query_meta_data meta)
312
+ : io_{ io }
313
+ , rows_{ std::move(rows) }
314
+ , meta_{ std::move(meta) }
315
+ {
316
+ }
317
+
318
+ void start(utils::movable_function<void(std::error_code)>&& on_ready) override
319
+ {
320
+ // The whole response is already in hand; there is no preamble to parse. Any query error is
321
+ // delivered at the terminal (mirroring the streaming path's trailing-error semantics).
322
+ asio::post(io_, [self = shared_from_this(), on_ready = std::move(on_ready)]() mutable {
323
+ on_ready({});
324
+ });
325
+ }
326
+
327
+ void next_row(
328
+ utils::movable_function<void(std::optional<std::string>, std::error_code)>&& handler) override
329
+ {
330
+ // Post the delivery so a consumer that pulls the next row from within its handler does not
331
+ // recurse synchronously through a large buffered result and overflow the stack.
332
+ asio::post(io_, [self = shared_from_this(), handler = std::move(handler)]() mutable {
333
+ if (self->cancelled_) {
334
+ return handler(std::nullopt, errc::common::request_canceled);
335
+ }
336
+ if (self->index_ < self->rows_.size()) {
337
+ // Each buffered row is consumed at most once, so hand it to the handler by move rather than
338
+ // copying what can be a large prepared-statement result out of the vector.
339
+ return handler(std::move(self->rows_[self->index_++]), {});
340
+ }
341
+ return handler(std::nullopt, operations::map_query_error(self->meta_));
342
+ });
343
+ }
344
+
345
+ [[nodiscard]] auto signature() const -> std::optional<std::string> override
346
+ {
347
+ return meta_.signature;
348
+ }
349
+
350
+ [[nodiscard]] auto meta_data() const
351
+ -> std::optional<operations::query_response::query_meta_data> override
352
+ {
353
+ return meta_;
354
+ }
355
+
356
+ [[nodiscard]] auto error_details() const -> stream_error_details override
357
+ {
358
+ // The response was parsed by the buffered path, so there is no raw JSON left to report; the
359
+ // service's error itself still is.
360
+ return extract_error_details(meta_, {});
361
+ }
362
+
363
+ auto set_deadline(std::chrono::time_point<std::chrono::steady_clock> /* deadline_tp */)
364
+ -> io::deadline_state override
365
+ {
366
+ // The prepared-statement path replays a buffered response. There is no socket to reclaim and
367
+ // next_row() always completes, so a deadline has nothing to bound.
368
+ return io::deadline_state::body_already_ended;
369
+ }
370
+
371
+ void cancel() override
372
+ {
373
+ cancelled_ = true;
374
+ }
375
+
376
+ private:
377
+ asio::io_context& io_;
378
+ std::vector<std::string> rows_;
379
+ operations::query_response::query_meta_data meta_;
380
+ std::size_t index_{ 0 };
381
+ // cancel() may be called from the consumer thread while the posted delivery reads it on the io
382
+ // thread, so it must be atomic. index_/rows_ are only touched on the io thread (posted handlers).
383
+ std::atomic_bool cancelled_{ false };
384
+ };
385
+ } // namespace
386
+
387
+ query_stream::query_stream(asio::io_context& io,
388
+ http_response_body body,
389
+ row_streamer_options options,
390
+ std::uint32_t http_status)
391
+ : impl_{
392
+ std::make_shared<streaming_query_stream_impl>(io, std::move(body), options, http_status)
393
+ }
394
+ {
395
+ }
396
+
397
+ query_stream::query_stream(asio::io_context& io,
398
+ std::vector<std::string> rows,
399
+ operations::query_response::query_meta_data meta)
400
+ : impl_{ std::make_shared<buffered_query_stream_impl>(io, std::move(rows), std::move(meta)) }
401
+ {
402
+ }
403
+
404
+ void
405
+ query_stream::start(utils::movable_function<void(std::error_code)>&& on_ready)
406
+ {
407
+ impl_->start(std::move(on_ready));
408
+ }
409
+
410
+ auto
411
+ query_stream::error_details() const -> stream_error_details
412
+ {
413
+ return impl_->error_details();
414
+ }
415
+
416
+ void
417
+ query_stream::next_row(
418
+ utils::movable_function<void(std::optional<std::string>, std::error_code)>&& handler)
419
+ {
420
+ impl_->next_row(std::move(handler));
421
+ }
422
+
423
+ auto
424
+ query_stream::signature() const -> std::optional<std::string>
425
+ {
426
+ return impl_->signature();
427
+ }
428
+
429
+ auto
430
+ query_stream::meta_data() const -> std::optional<operations::query_response::query_meta_data>
431
+ {
432
+ return impl_->meta_data();
433
+ }
434
+
435
+ auto
436
+ query_stream::set_deadline(std::chrono::time_point<std::chrono::steady_clock> deadline_tp)
437
+ -> io::deadline_state
438
+ {
439
+ return impl_->set_deadline(deadline_tp);
440
+ }
441
+
442
+ void
443
+ query_stream::cancel()
444
+ {
445
+ impl_->cancel();
446
+ }
447
+ } // namespace couchbase::core
@@ -0,0 +1,141 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2026. Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #include "core/io/stream_deadline.hxx"
21
+ #include "core/operations/document_query.hxx"
22
+ #include "row_streamer.hxx"
23
+ #include "stream_error_details.hxx"
24
+ #include "utils/movable_function.hxx"
25
+
26
+ #include <chrono>
27
+ #include <cstdint>
28
+ #include <memory>
29
+ #include <optional>
30
+ #include <string>
31
+ #include <system_error>
32
+ #include <vector>
33
+
34
+ namespace asio
35
+ {
36
+ class io_context;
37
+ } // namespace asio
38
+
39
+ namespace couchbase::core
40
+ {
41
+ class query_stream_impl;
42
+ class http_response_body;
43
+
44
+ /**
45
+ * L3 streaming state machine for N1QL query responses.
46
+ *
47
+ * Wraps a row_streamer (pointed at "/results/^") and adds query-specific semantics:
48
+ * preamble parsing (signature + upfront error detection), late trailer metadata, and
49
+ * terminal error classification via map_query_error.
50
+ */
51
+ class query_stream
52
+ {
53
+ public:
54
+ /**
55
+ * Constructs an empty handle (no underlying stream). Used to carry the "no stream" outcome on
56
+ * the error path of the dispatch; none of the streaming methods may be called on it.
57
+ */
58
+ query_stream() = default;
59
+
60
+ /**
61
+ * http_status is the status of the response the body belongs to; an HTTP 400 that matches no
62
+ * specific error is reported as invalid_argument.
63
+ */
64
+ query_stream(asio::io_context& io,
65
+ http_response_body body,
66
+ row_streamer_options options = {},
67
+ std::uint32_t http_status = 0);
68
+
69
+ /**
70
+ * Constructs a stream that replays an already-buffered response (rows are row-JSON strings,
71
+ * metadata already parsed) without any JSON re-parsing. Used for the prepared-statement path,
72
+ * which must run buffered but presents the uniform streaming handle.
73
+ */
74
+ query_stream(asio::io_context& io,
75
+ std::vector<std::string> rows,
76
+ operations::query_response::query_meta_data meta);
77
+
78
+ /**
79
+ * Starts the stream. Resolves once the preamble has been parsed. The early_error is set when
80
+ * the response carried an upfront error (or the preamble failed to parse).
81
+ */
82
+ void start(utils::movable_function<void(std::error_code early_error)>&& on_ready);
83
+
84
+ /**
85
+ * Service-reported diagnostics from the most recently parsed JSON section — the preamble after
86
+ * start(), the trailer once the terminal has been reached. The dispatch layer stamps these into
87
+ * the error context so a streaming failure reports the same detail as a buffered one.
88
+ */
89
+ [[nodiscard]] auto error_details() const -> stream_error_details;
90
+
91
+ /**
92
+ * Retrieves the next row. A populated row is delivered with a falsy error_code. An empty row
93
+ * (std::nullopt) signals the end: a falsy error_code means a clean success end, a truthy
94
+ * error_code means a trailing query/transport error.
95
+ */
96
+ void next_row(
97
+ utils::movable_function<void(std::optional<std::string> row, std::error_code)>&& handler);
98
+
99
+ /**
100
+ * Signature captured from the preamble (available after start resolves).
101
+ */
102
+ [[nodiscard]] auto signature() const -> std::optional<std::string>;
103
+
104
+ /**
105
+ * Trailer metadata. Valid only after the stream has reached its end.
106
+ */
107
+ [[nodiscard]] auto meta_data() const
108
+ -> std::optional<operations::query_response::query_meta_data>;
109
+
110
+ /**
111
+ * Closes the HTTP body at `deadline_tp`, whether or not a row is being pulled, releasing the
112
+ * connection where one is still held. A response received in full holds none: once its bytes
113
+ * have also been parsed the call arms nothing and reports body_already_ended, and while bytes
114
+ * remain unparsed it ends the body, dropping those and reporting the timeout. Rows already
115
+ * parsed stay consumable either way. The inter-read idle timer is armed only while a socket
116
+ * read is in flight, and a consumer stopped above the high-water mark has none.
117
+ *
118
+ * The terminal reaches the consumer through a pull. A pull outstanding at expiry completes
119
+ * with the timeout. A read that completes the response is the exception: the whole response
120
+ * arrived, so those bytes are delivered as a clean end. Otherwise the next pull reports the
121
+ * terminal, after any rows already buffered.
122
+ *
123
+ * Optional. The terminal is unambiguous_timeout for a read-only request, ambiguous_timeout
124
+ * otherwise. Re-arming before expiry replaces the deadline. Once the stream has ended a
125
+ * further call arms nothing and reports body_already_ended. Callable from any thread.
126
+ *
127
+ * A stream replaying an already-buffered response holds no socket, so a deadline on it is a
128
+ * no-op.
129
+ */
130
+ [[nodiscard]] auto set_deadline(std::chrono::time_point<std::chrono::steady_clock> deadline_tp)
131
+ -> io::deadline_state;
132
+
133
+ /**
134
+ * Cancels the stream & closes the HTTP connection.
135
+ */
136
+ void cancel();
137
+
138
+ private:
139
+ std::shared_ptr<query_stream_impl> impl_;
140
+ };
141
+ } // namespace couchbase::core