aws_advanced_ruby_driver_wrapper 1.0.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 (150) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +175 -0
  4. data/NOTICE +1 -0
  5. data/README.md +168 -0
  6. data/THIRD-PARTY-LICENSES +473 -0
  7. data/aws_advanced_ruby_driver_wrapper.gemspec +73 -0
  8. data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_mysql2_adapter.rb +73 -0
  9. data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_postgresql_adapter.rb +95 -0
  10. data/lib/aws_advanced_ruby_driver_wrapper/custom_configuration.rb +58 -0
  11. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_mysql_dialect.rb +103 -0
  12. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_pg_dialect.rb +124 -0
  13. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/dialect_codes.rb +38 -0
  14. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_mysql_dialect.rb +91 -0
  15. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_pg_dialect.rb +92 -0
  16. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_mysql_dialect.rb +95 -0
  17. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_pg_dialect.rb +86 -0
  18. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/mysql_dialect.rb +98 -0
  19. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/pg_dialect.rb +95 -0
  20. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_mysql_dialect.rb +88 -0
  21. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_pg_dialect.rb +86 -0
  22. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/unknown_dialect.rb +72 -0
  23. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/utils/dialect_utils.rb +71 -0
  24. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect.rb +154 -0
  25. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect_manager.rb +55 -0
  26. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/mysql_driver_dialect.rb +165 -0
  27. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/pg_driver_dialect.rb +201 -0
  28. data/lib/aws_advanced_ruby_driver_wrapper/errors/error_handler.rb +62 -0
  29. data/lib/aws_advanced_ruby_driver_wrapper/errors/mysql_error_handler.rb +80 -0
  30. data/lib/aws_advanced_ruby_driver_wrapper/errors/pg_error_handler.rb +126 -0
  31. data/lib/aws_advanced_ruby_driver_wrapper/errors.rb +59 -0
  32. data/lib/aws_advanced_ruby_driver_wrapper/host/connection_string_host_list_provider.rb +95 -0
  33. data/lib/aws_advanced_ruby_driver_wrapper/host/global_aurora_host_list_provider.rb +65 -0
  34. data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability.rb +24 -0
  35. data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability_strategy.rb +27 -0
  36. data/lib/aws_advanced_ruby_driver_wrapper/host/host_info.rb +137 -0
  37. data/lib/aws_advanced_ruby_driver_wrapper/host/host_role.rb +25 -0
  38. data/lib/aws_advanced_ruby_driver_wrapper/host/random_host_selector.rb +40 -0
  39. data/lib/aws_advanced_ruby_driver_wrapper/host/rds_host_list_provider.rb +206 -0
  40. data/lib/aws_advanced_ruby_driver_wrapper/logging.rb +110 -0
  41. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/cluster_topology_monitor.rb +709 -0
  42. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/global_cluster_topology_monitor.rb +72 -0
  43. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor.rb +99 -0
  44. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_connection.rb +57 -0
  45. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_state.rb +25 -0
  46. data/lib/aws_advanced_ruby_driver_wrapper/mysql.rb +429 -0
  47. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/blue_green_plugin.rb +205 -0
  48. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/host_mapper.rb +132 -0
  49. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/iam_host_tracker.rb +84 -0
  50. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interim_status.rb +92 -0
  51. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interval_rate.rb +27 -0
  52. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase.rb +69 -0
  53. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_event_log.rb +85 -0
  54. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_time_info.rb +25 -0
  55. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/role.rb +38 -0
  56. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/base_routing.rb +83 -0
  57. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/reject_connect_routing.rb +40 -0
  58. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/substitute_connect_routing.rb +136 -0
  59. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_connect_routing.rb +53 -0
  60. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_execute_routing.rb +52 -0
  61. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_until_corresponding_host_found_connect_routing.rb +83 -0
  62. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status.rb +68 -0
  63. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_builder.rb +244 -0
  64. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_info.rb +30 -0
  65. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_monitor.rb +564 -0
  66. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_provider.rb +414 -0
  67. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_state.rb +98 -0
  68. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_timer.rb +46 -0
  69. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_monitor.rb +266 -0
  70. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_plugin.rb +158 -0
  71. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/info.rb +111 -0
  72. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/member_list_type.rb +31 -0
  73. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/role.rb +45 -0
  74. data/lib/aws_advanced_ruby_driver_wrapper/plugins/default_plugin.rb +108 -0
  75. data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_mode.rb +43 -0
  76. data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_plugin.rb +467 -0
  77. data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_mode.rb +68 -0
  78. data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_plugin.rb +403 -0
  79. data/lib/aws_advanced_ruby_driver_wrapper/plugins/iam_auth_plugin.rb +159 -0
  80. data/lib/aws_advanced_ruby_driver_wrapper/plugins/initial_connection_strategy_plugin.rb +485 -0
  81. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/audit_logger.rb +157 -0
  82. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_cipher.rb +159 -0
  83. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_encryption_config.rb +61 -0
  84. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/connection_source.rb +91 -0
  85. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/data_key_cache.rb +220 -0
  86. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_algorithm.rb +75 -0
  87. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_config.rb +146 -0
  88. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_service.rb +391 -0
  89. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/error_context.rb +198 -0
  90. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/errors.rb +259 -0
  91. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_management_utility.rb +435 -0
  92. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_manager.rb +378 -0
  93. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_metadata.rb +86 -0
  94. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_plugin.rb +890 -0
  95. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_utility.rb +281 -0
  96. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/metadata_manager.rb +332 -0
  97. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sanitizer.rb +147 -0
  98. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_name.rb +70 -0
  99. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_validator.rb +211 -0
  100. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sql_runner.rb +147 -0
  101. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/type_marker.rb +109 -0
  102. data/lib/aws_advanced_ruby_driver_wrapper/plugins/secrets_manager_plugin.rb +358 -0
  103. data/lib/aws_advanced_ruby_driver_wrapper/postgresql.rb +659 -0
  104. data/lib/aws_advanced_ruby_driver_wrapper/property_definition.rb +409 -0
  105. data/lib/aws_advanced_ruby_driver_wrapper/ruby_method.rb +122 -0
  106. data/lib/aws_advanced_ruby_driver_wrapper/services/connection_service.rb +143 -0
  107. data/lib/aws_advanced_ruby_driver_wrapper/services/dialect_service.rb +267 -0
  108. data/lib/aws_advanced_ruby_driver_wrapper/services/host_service.rb +199 -0
  109. data/lib/aws_advanced_ruby_driver_wrapper/services/monitor_service.rb +186 -0
  110. data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_call_context.rb +63 -0
  111. data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_manager.rb +273 -0
  112. data/lib/aws_advanced_ruby_driver_wrapper/services/service_container.rb +30 -0
  113. data/lib/aws_advanced_ruby_driver_wrapper/services/service_utility.rb +78 -0
  114. data/lib/aws_advanced_ruby_driver_wrapper/services/session_state_service.rb +56 -0
  115. data/lib/aws_advanced_ruby_driver_wrapper/utils/accessible_regions.rb +52 -0
  116. data/lib/aws_advanced_ruby_driver_wrapper/utils/ar_constants.rb +25 -0
  117. data/lib/aws_advanced_ruby_driver_wrapper/utils/aurora_topology_utils.rb +99 -0
  118. data/lib/aws_advanced_ruby_driver_wrapper/utils/aws_credentials_utils.rb +62 -0
  119. data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config.rb +91 -0
  120. data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config_parser.rb +368 -0
  121. data/lib/aws_advanced_ruby_driver_wrapper/utils/conversion_utils.rb +51 -0
  122. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/batching_event_publisher.rb +119 -0
  123. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/data_access_event.rb +26 -0
  124. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/monitor_reset_event.rb +26 -0
  125. data/lib/aws_advanced_ruby_driver_wrapper/utils/global_aurora_topology_utils.rb +185 -0
  126. data/lib/aws_advanced_ruby_driver_wrapper/utils/host_list_utils.rb +27 -0
  127. data/lib/aws_advanced_ruby_driver_wrapper/utils/iam_auth_utils.rb +112 -0
  128. data/lib/aws_advanced_ruby_driver_wrapper/utils/multi_az_topology_utils.rb +117 -0
  129. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/encryption_annotation_parser.rb +99 -0
  130. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/mysql_statement_analyzer.rb +641 -0
  131. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/pg_statement_analyzer.rb +502 -0
  132. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_analysis.rb +63 -0
  133. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_type.rb +35 -0
  134. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint.rb +27 -0
  135. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint_parser.rb +50 -0
  136. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/sql_parser.rb +139 -0
  137. data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_url_type.rb +71 -0
  138. data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_utils.rb +575 -0
  139. data/lib/aws_advanced_ruby_driver_wrapper/utils/retry_util.rb +153 -0
  140. data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_encoding.rb +56 -0
  141. data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_method_analyzer.rb +195 -0
  142. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/cache_entry.rb +56 -0
  143. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/expiration_cache.rb +108 -0
  144. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/sliding_expiration_cache.rb +137 -0
  145. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/storage_service.rb +172 -0
  146. data/lib/aws_advanced_ruby_driver_wrapper/utils/topology_utils.rb +127 -0
  147. data/lib/aws_advanced_ruby_driver_wrapper/version.rb +19 -0
  148. data/lib/aws_advanced_ruby_driver_wrapper/wrapper_property.rb +64 -0
  149. data/lib/aws_advanced_ruby_driver_wrapper.rb +116 -0
  150. metadata +227 -0
@@ -0,0 +1,659 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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
+ # Loads the gem's top-level setup (version constant, global configuration) that this client relies on,
18
+ # so requiring this file alone is enough to use the PostgreSQL client.
19
+ require_relative '../aws_advanced_ruby_driver_wrapper'
20
+ require_relative 'utils/connection_config_parser'
21
+ require_relative 'utils/sql_encoding'
22
+ require_relative 'services/service_utility'
23
+ require_relative 'ruby_method'
24
+ require_relative 'errors'
25
+
26
+ module AwsAdvancedRubyDriverWrapper
27
+ class WrapperPgConnection
28
+ class << self
29
+ def new(*, **)
30
+ instance = allocate
31
+ instance.send(:initialize, *, **)
32
+ return instance unless block_given?
33
+
34
+ begin
35
+ yield instance
36
+ ensure
37
+ instance.close
38
+ end
39
+ end
40
+
41
+ alias open new
42
+ alias connect new
43
+ end
44
+
45
+ def initialize(*, **)
46
+ ensure_pg!
47
+ config = Utils::ConnectionConfigParser.parse(:postgresql, *, **)
48
+ @service_container = Services::ServiceUtility.create_standard_container(config)
49
+ @service_container.host_service.refresh_host_list
50
+ @prepared_on = {}
51
+ @prepared_sql = {}
52
+ @async_conn = nil
53
+ @async_sql = nil
54
+ @copy_conn = nil
55
+ @copy_sql = nil
56
+ @lo_conn = nil
57
+ conn_service = @service_container.connection_service
58
+ @service_container.plugin_manager.connect(conn_service.initial_host_info, conn_service.driver_props, true)
59
+ end
60
+
61
+ # Every canonical pg operation that talks to the server, and everything this class has to know about one: the
62
+ # name the plugins see it under, the connection it is bound to, where the SQL it carries is among its
63
+ # arguments, and the steps that should be performed after.
64
+ #
65
+ # An operation with no +sql_at+ takes the SQL of whatever it is bound to: the statement a prepared
66
+ # operation names, the statement a pending exchange was started with, or the statement a COPY was
67
+ # opened with. That is the whole reason each of them is remembered, since none is among the arguments
68
+ # of the call that reads it back.
69
+ OPERATIONS = {
70
+ exec: { method: RubyMethod::CONNECTION_EXEC, sql_at: 0, after: :remember_sql_prepared },
71
+ async_exec: { method: RubyMethod::CONNECTION_ASYNC_EXEC, sql_at: 0, after: :remember_sql_prepared },
72
+ exec_params: { method: RubyMethod::CONNECTION_EXEC_PARAMS, sql_at: 0, after: :remember_sql_prepared },
73
+ transaction: { method: RubyMethod::CONNECTION_TRANSACTION },
74
+ close: { method: RubyMethod::CONNECTION_CLOSE },
75
+ reset: { method: RubyMethod::CONNECTION_RESET, after: :reset_session_state },
76
+ reset_start: { method: RubyMethod::CONNECTION_RESET_START },
77
+ reset_poll: { method: RubyMethod::CONNECTION_RESET_POLL },
78
+
79
+ # A prepared statement only exists on the connection it was prepared on.
80
+ prepare: { method: RubyMethod::CONNECTION_PREPARE, sql_at: 1, after: :remember_prepared },
81
+ send_prepare: { method: RubyMethod::CONNECTION_SEND_PREPARE, sql_at: 1, after: %i[remember_prepared remember_async] },
82
+ exec_prepared: { method: RubyMethod::CONNECTION_EXEC_PREPARED, bound_to: :prepared },
83
+ describe_prepared: { method: RubyMethod::CONNECTION_DESCRIBE_PREPARED, bound_to: :prepared },
84
+ send_query_prepared: { method: RubyMethod::CONNECTION_SEND_QUERY_PREPARED, bound_to: :prepared, after: :remember_async },
85
+ send_describe_prepared: { method: RubyMethod::CONNECTION_SEND_DESCRIBE_PREPARED, bound_to: :prepared, after: :remember_async },
86
+ close_prepared: { method: RubyMethod::CONNECTION_CLOSE_PREPARED, bound_to: :prepared, after: :forget_prepared },
87
+
88
+ # A pending exchange, and the portal it may have left, can only be continued on the connection it
89
+ # was started on.
90
+ send_query: { method: RubyMethod::CONNECTION_SEND_QUERY, sql_at: 0, after: %i[remember_async remember_sql_prepared] },
91
+ send_query_params: { method: RubyMethod::CONNECTION_SEND_QUERY_PARAMS, sql_at: 0,
92
+ after: %i[remember_async remember_sql_prepared] },
93
+ get_result: { method: RubyMethod::CONNECTION_GET_RESULT, bound_to: :async, after: :forget_async_when_drained },
94
+ get_last_result: { method: RubyMethod::CONNECTION_GET_LAST_RESULT, bound_to: :async, after: :forget_async },
95
+ describe_portal: { method: RubyMethod::CONNECTION_DESCRIBE_PORTAL, bound_to: :async },
96
+ close_portal: { method: RubyMethod::CONNECTION_CLOSE_PORTAL, bound_to: :async },
97
+ send_describe_portal: { method: RubyMethod::CONNECTION_SEND_DESCRIBE_PORTAL, bound_to: :async, after: :remember_async },
98
+ send_flush_request: { method: RubyMethod::CONNECTION_SEND_FLUSH_REQUEST, bound_to: :async, after: :remember_async },
99
+ pipeline_sync: { method: RubyMethod::CONNECTION_PIPELINE_SYNC, bound_to: :async },
100
+ send_pipeline_sync: { method: RubyMethod::CONNECTION_SEND_PIPELINE_SYNC, bound_to: :async, after: :remember_async },
101
+ discard_results: { method: RubyMethod::CONNECTION_DISCARD_RESULTS, bound_to: :async, after: :forget_async },
102
+ block: { method: RubyMethod::CONNECTION_BLOCK, bound_to: :async },
103
+
104
+ # A COPY can only be fed or read on the connection it was started on.
105
+ copy_data: { method: RubyMethod::CONNECTION_COPY_DATA, sql_at: 0 },
106
+ put_copy_data: { method: RubyMethod::CONNECTION_PUT_COPY_DATA, bound_to: :copy },
107
+ get_copy_data: { method: RubyMethod::CONNECTION_GET_COPY_DATA, bound_to: :copy },
108
+ put_copy_end: { method: RubyMethod::CONNECTION_PUT_COPY_END, bound_to: :copy, after: :forget_copy },
109
+
110
+ cancel: { method: RubyMethod::CONNECTION_CANCEL },
111
+ flush: { method: RubyMethod::CONNECTION_FLUSH },
112
+ consume_input: { method: RubyMethod::CONNECTION_CONSUME_INPUT },
113
+ notifies: { method: RubyMethod::CONNECTION_NOTIFIES },
114
+ wait_for_notify: { method: RubyMethod::CONNECTION_WAIT_FOR_NOTIFY },
115
+ encrypt_password: { method: RubyMethod::CONNECTION_ENCRYPT_PASSWORD },
116
+ set_client_encoding: { method: RubyMethod::CONNECTION_SET_CLIENT_ENCODING },
117
+ set_default_encoding: { method: RubyMethod::CONNECTION_SET_DEFAULT_ENCODING },
118
+ 'internal_encoding=': { method: RubyMethod::CONNECTION_INTERNAL_ENCODING_SET },
119
+
120
+ # A large object descriptor is only open on the connection that opened it.
121
+ lo_creat: { method: RubyMethod::CONNECTION_LO_CREAT },
122
+ lo_create: { method: RubyMethod::CONNECTION_LO_CREATE },
123
+ lo_import: { method: RubyMethod::CONNECTION_LO_IMPORT },
124
+ lo_export: { method: RubyMethod::CONNECTION_LO_EXPORT },
125
+ lo_unlink: { method: RubyMethod::CONNECTION_LO_UNLINK },
126
+ lo_open: { method: RubyMethod::CONNECTION_LO_OPEN, after: :remember_large_object },
127
+ lo_read: { method: RubyMethod::CONNECTION_LO_READ, bound_to: :large_object },
128
+ lo_write: { method: RubyMethod::CONNECTION_LO_WRITE, bound_to: :large_object },
129
+ lo_lseek: { method: RubyMethod::CONNECTION_LO_LSEEK, bound_to: :large_object },
130
+ lo_tell: { method: RubyMethod::CONNECTION_LO_TELL, bound_to: :large_object },
131
+ lo_truncate: { method: RubyMethod::CONNECTION_LO_TRUNCATE, bound_to: :large_object },
132
+ lo_close: { method: RubyMethod::CONNECTION_LO_CLOSE, bound_to: :large_object, after: :forget_large_object }
133
+ }.freeze
134
+
135
+ # pg gives most operations more than one spelling, and an application is free to use any of them. Each
136
+ # spelling here is mapped to the operation it performs, so that spellings enter the pipeline under one
137
+ # canonical name and get the same +bound_to+, +sql_at+ and +after+ handling its {OPERATIONS} entry asks
138
+ # for. Only the pipeline name is shared: the driver is still called under the original spelling.
139
+ #
140
+ # For example, the +sync_+ and +async_+ forms of an operation are two different calls, the first
141
+ # blocking in libpq and the second sending and then waiting on the socket from Ruby, where the
142
+ # wait can be interrupted. Which of the two a bare +exec+ means is itself settable, through
143
+ # +PG::Connection.async_api=+.
144
+ #
145
+ # A +sync_+ or +async_+ spelling that is missing here is still recognized, by {#operation_for}
146
+ # removing the prefix. The current spellings are written out here anyway, so that the list can
147
+ # be checked against the gem.
148
+ OPERATION_BY_SPELLING = {
149
+ async_query: :exec, sync_exec: :exec,
150
+ async_exec_params: :exec_params, sync_exec_params: :exec_params,
151
+ async_exec_prepared: :exec_prepared, sync_exec_prepared: :exec_prepared,
152
+ async_prepare: :prepare, sync_prepare: :prepare,
153
+ async_describe_prepared: :describe_prepared, sync_describe_prepared: :describe_prepared,
154
+ async_describe_portal: :describe_portal, sync_describe_portal: :describe_portal,
155
+ async_get_result: :get_result, sync_get_result: :get_result,
156
+ async_get_last_result: :get_last_result, sync_get_last_result: :get_last_result,
157
+ async_put_copy_data: :put_copy_data, sync_put_copy_data: :put_copy_data,
158
+ async_get_copy_data: :get_copy_data, sync_get_copy_data: :get_copy_data,
159
+ async_put_copy_end: :put_copy_end, sync_put_copy_end: :put_copy_end,
160
+ async_reset: :reset, sync_reset: :reset,
161
+ async_cancel: :cancel, sync_cancel: :cancel,
162
+ async_flush: :flush, sync_flush: :flush,
163
+ async_close_prepared: :close_prepared, sync_close_prepared: :close_prepared,
164
+ async_close_portal: :close_portal, sync_close_portal: :close_portal,
165
+ async_pipeline_sync: :pipeline_sync, sync_pipeline_sync: :pipeline_sync,
166
+ async_encrypt_password: :encrypt_password, sync_encrypt_password: :encrypt_password,
167
+ async_set_client_encoding: :set_client_encoding, sync_set_client_encoding: :set_client_encoding,
168
+ 'client_encoding=': :set_client_encoding,
169
+ notifies_wait: :wait_for_notify,
170
+ locreat: :lo_creat, locreate: :lo_create, loimport: :lo_import, loexport: :lo_export,
171
+ lounlink: :lo_unlink, loopen: :lo_open, loread: :lo_read, lowrite: :lo_write,
172
+ loclose: :lo_close, lolseek: :lo_lseek, lo_seek: :lo_lseek, loseek: :lo_lseek,
173
+ lotell: :lo_tell, lotruncate: :lo_truncate
174
+ }.freeze
175
+
176
+ # A statement can also be prepared by sending a +PREPARE+ rather than by calling pg's own
177
+ # +prepare+, and the +exec_prepared+ that runs it looks no different either way. The two are read
178
+ # here so that a statement prepared the first way is remembered like one prepared the second, and
179
+ # a plugin that has to inspect the statement a call runs still has it to look at.
180
+ #
181
+ # The name is an identifier, so an unquoted one is folded to lower case. The parameter types in
182
+ # front of +AS+ are optional. What follows +AS+ is the statement, to the end of the string, which
183
+ # a +PREPARE+ shares with nothing else unless the caller sent more than one statement at once.
184
+ #
185
+ # Read with a pattern rather than a parse because this sits on the path of every statement the
186
+ # connection sends, and the two pieces wanted here are a name and everything after +AS+.
187
+ # +Utils::Parser::PgStatementAnalyzer+ reads the same construct properly, from the parse tree, for
188
+ # the plugin that has to know what the carried statement writes.
189
+ STATEMENT_NAME = /"(?:[^"]|"")+"|\w+/
190
+ SQL_PREPARE = /\A\s*PREPARE\s+(#{STATEMENT_NAME})\s*(?:\([^)]*\)\s*)?AS\s+(.+)\z/im
191
+ # +DEALLOCATE [PREPARE] { name | ALL }+ un-prepares what a +PREPARE+ prepared, which is what
192
+ # +close_prepared+ does to a statement prepared through the driver.
193
+ SQL_DEALLOCATE = /\A\s*DEALLOCATE\s+(?:PREPARE\s+)?(#{STATEMENT_NAME})\s*;?\s*\z/im
194
+
195
+ # Explicitly define critical methods (bypass method_missing to avoid method_missing overhead).
196
+
197
+ def exec(sql, *params)
198
+ execute_operation(:exec, [sql, *params])
199
+ end
200
+
201
+ # pg spells this operation +exec+, +query+, +async_exec+ and +async_query+, all of which run the
202
+ # same libpq call. +query+ is defined here rather than left to method_missing because it is the
203
+ # spelling applications use most after +exec+, and it enters the pipeline as +connection.exec+,
204
+ # since that is the libpq operation being performed.
205
+ def query(sql, *params)
206
+ execute_operation(:exec, [sql, *params], spelling: :query)
207
+ end
208
+
209
+ def exec_params(sql, params, result_format = 0, type_map = nil)
210
+ execute_operation(:exec_params, [sql, params, result_format, type_map])
211
+ end
212
+
213
+ def async_exec(sql, *params)
214
+ execute_operation(:async_exec, [sql, *params])
215
+ end
216
+
217
+ def transaction(&)
218
+ execute_operation(:transaction, &)
219
+ end
220
+
221
+ def close
222
+ execute_operation(:close)
223
+ end
224
+
225
+ alias finish close
226
+
227
+ # Resets the connection through the pipeline and returns this wrapper, so the reset connection stays
228
+ # usable through it. The driver's own reset tears down and re-establishes the underlying socket, which
229
+ # clears any server-side session state, so the tracked session state is reset to match.
230
+ def reset
231
+ execute_operation(:reset)
232
+ self
233
+ end
234
+
235
+ # -- Prepared statements --
236
+
237
+ def prepare(stmt_name, sql, param_types = nil)
238
+ execute_operation(:prepare, [stmt_name, sql, param_types])
239
+ end
240
+
241
+ def send_prepare(stmt_name, sql, param_types = nil)
242
+ execute_operation(:send_prepare, [stmt_name, sql, param_types])
243
+ end
244
+
245
+ def exec_prepared(stmt_name, params = [], result_format = 0, type_map = nil)
246
+ execute_operation(:exec_prepared, [stmt_name, params, result_format, type_map])
247
+ end
248
+
249
+ def describe_prepared(stmt_name)
250
+ execute_operation(:describe_prepared, [stmt_name])
251
+ end
252
+
253
+ def send_query_prepared(stmt_name, params = [], result_format = 0, type_map = nil)
254
+ execute_operation(:send_query_prepared, [stmt_name, params, result_format, type_map])
255
+ end
256
+
257
+ # -- Pending exchanges --
258
+
259
+ def send_query(sql, *params)
260
+ execute_operation(:send_query, [sql, *params])
261
+ end
262
+
263
+ def send_query_params(sql, params, result_format = 0, type_map = nil)
264
+ execute_operation(:send_query_params, [sql, params, result_format, type_map])
265
+ end
266
+
267
+ def get_result # rubocop:disable Naming/AccessorMethodName
268
+ execute_operation(:get_result)
269
+ end
270
+
271
+ def get_last_result # rubocop:disable Naming/AccessorMethodName
272
+ execute_operation(:get_last_result)
273
+ end
274
+
275
+ # -- COPY --
276
+
277
+ # The connection and the statement are held for as long as the block runs and let go afterward even
278
+ # if the block raises. The rows the block feeds or reads belong to that statement, and it is the only
279
+ # place they are named, so it is what the calls inside the block publish. It is held as the copy
280
+ # plugins inspect, made once here rather than on every row.
281
+ def copy_data(sql, coder = nil, &)
282
+ @copy_conn = current_conn
283
+ @copy_sql = Utils::SqlEncoding.inspectable(sql)
284
+ execute_operation(:copy_data, [sql, coder], &)
285
+ ensure
286
+ @copy_conn = nil
287
+ @copy_sql = nil
288
+ end
289
+
290
+ def put_copy_data(buffer, encoder = nil)
291
+ execute_operation(:put_copy_data, [buffer, encoder])
292
+ end
293
+
294
+ def get_copy_data(async = false, decoder = nil)
295
+ execute_operation(:get_copy_data, [async, decoder])
296
+ end
297
+
298
+ def put_copy_end(error_message = nil)
299
+ execute_operation(:put_copy_end, [error_message])
300
+ end
301
+
302
+ # -- method_missing: covers non-network calls, the other spellings pg gives a call, and the network
303
+ # calls that are rare enough not to be worth a method of their own --
304
+
305
+ def method_missing(method_name, *args, **kwargs, &)
306
+ conn = current_conn
307
+ raise NoMethodError, 'Connection not initialized' if conn.nil?
308
+ raise NoMethodError, "undefined method `#{method_name}' for #{self.class}" unless conn.respond_to?(method_name)
309
+
310
+ operation = operation_for(method_name)
311
+ return conn.send(method_name, *args, **kwargs, &) if operation.nil?
312
+
313
+ execute_operation(operation, args, kwargs, spelling: method_name, &)
314
+ end
315
+
316
+ def respond_to_missing?(method, include_private = false)
317
+ OPERATION_BY_SPELLING.key?(method) || current_conn.respond_to?(method, include_private) || super
318
+ end
319
+
320
+ # A concise representation that never exposes the connection config (which carries
321
+ # credentials) or the cached SQL text this instance holds. Defined so a default
322
+ # dump - via logging, interpolation, `pp`, or a backtrace - cannot leak either.
323
+ def inspect
324
+ format('#<%<class>s:0x%<addr>016x>', class: self.class.name, addr: object_id << 1)
325
+ end
326
+ alias to_s inspect
327
+
328
+ def pretty_print(pp)
329
+ pp.text(inspect)
330
+ end
331
+
332
+ private
333
+
334
+ # Runs one canonical operation through the pipeline, passing the operation's `bound_to` connection and
335
+ # the SQL it carries, and performing any `after` steps as necessary. The SQL is read before the call is
336
+ # made, since an `after` step may be what forgets it, and it is handed to the result as well, so that a
337
+ # plugin which has to inspect the statement still sees it when the rows are read.
338
+ def execute_operation(operation, args = [], kwargs = {}, spelling: operation, &)
339
+ spec = OPERATIONS[operation] || { method: "connection.#{operation}" }
340
+ conn = current_conn
341
+ # Guard against a missing connection. Fail loudly instead.
342
+ raise NoMethodError, 'Connection not initialized' if conn.nil?
343
+
344
+ # Only forward keyword arguments when there are any.
345
+ sql = sql_for(spec, args)
346
+ result =
347
+ if kwargs.empty?
348
+ pm.execute(
349
+ spec[:method], conn,
350
+ ->(*a, &b) { current_conn.public_send(spelling, *a, &b) },
351
+ *args, bounded_conn: bounded_conn_for(spec[:bound_to], args), sql: sql, &
352
+ )
353
+ else
354
+ pm.execute(
355
+ spec[:method], conn,
356
+ ->(*a, **opts, &b) { current_conn.public_send(spelling, *a, **opts, &b) },
357
+ *args, **kwargs, bounded_conn: bounded_conn_for(spec[:bound_to], args), sql: sql, &
358
+ )
359
+ end
360
+ Array(spec[:after]).each { |hook| send(hook, args, result, sql) }
361
+ wrap_pg_result(result, sql)
362
+ end
363
+
364
+ # The operation a call performs, whatever spelling it arrived under, or nil for a call that does not
365
+ # talk to the server and so has no business in the pipeline.
366
+ def operation_for(spelling)
367
+ operation = OPERATION_BY_SPELLING[spelling] || spelling
368
+ return operation if OPERATIONS.key?(operation)
369
+
370
+ # A spelling pg has added since {OPERATION_BY_SPELLING} was written. +sync_+ and +async_+ are its
371
+ # own prefixes for the two ways it performs an operation, so whatever is left once one of them is
372
+ # removed names that operation.
373
+ stripped = spelling.to_s.sub(/\A(a?sync)_/, '').to_sym
374
+ return stripped if OPERATIONS.key?(stripped)
375
+
376
+ # Listed by the dialect but not described in {OPERATIONS}, which means the two have got out of
377
+ # step. It still talks to the server, so it still goes through the plugins, under a name they can
378
+ # match on, though without the bounded connection check that only a named method gets.
379
+ spelling if network_bound_methods.include?("connection.#{spelling}")
380
+ end
381
+
382
+ # @return [Object, nil] the connection the operation is bound to, if it is bound to one
383
+ def bounded_conn_for(bound_to, args)
384
+ case bound_to
385
+ when :prepared then @prepared_on[args.first]
386
+ when :async then @async_conn
387
+ when :copy then @copy_conn
388
+ when :large_object then @lo_conn
389
+ end
390
+ end
391
+
392
+ # @return [String, nil] the SQL the operation carries, taken from its arguments when it names a
393
+ # statement of its own and from whatever it is bound to when it does not, as a copy that is
394
+ # safe to inspect
395
+ def sql_for(spec, args)
396
+ sql =
397
+ if spec[:sql_at]
398
+ args[spec[:sql_at]]
399
+ else
400
+ case spec[:bound_to]
401
+ when :prepared then @prepared_sql[args.first]
402
+ when :async then @async_sql
403
+ when :copy then @copy_sql
404
+ end
405
+ end
406
+ Utils::SqlEncoding.inspectable(sql)
407
+ end
408
+
409
+ # -- What an operation leaves behind, named by the +after+ entries of {OPERATIONS} --
410
+
411
+ # A reset re-establishes the underlying socket, dropping any server-side session state (open
412
+ # transaction, autocommit setting), so the tracked state is reset to match the fresh connection.
413
+ def reset_session_state(_args, _result, _sql)
414
+ @service_container.session_state_service.reset
415
+ end
416
+
417
+ def remember_prepared(args, _result, sql)
418
+ @prepared_on[args.first] = current_conn
419
+ @prepared_sql[args.first] = sql
420
+ end
421
+
422
+ def forget_prepared(args, _result, _sql)
423
+ @prepared_on.delete(args.first)
424
+ @prepared_sql.delete(args.first)
425
+ end
426
+
427
+ # A +PREPARE+ or +DEALLOCATE+ that was sent as a statement, treated as the +prepare+ or the
428
+ # +close_prepared+ it amounts to. Anything else that was sent is left alone.
429
+ def remember_sql_prepared(_args, _result, sql)
430
+ return unless sql.is_a?(String)
431
+
432
+ if (prepared = SQL_PREPARE.match(sql))
433
+ remember_prepared([statement_name_of(prepared[1])], nil, prepared[2].strip)
434
+ elsif (deallocated = SQL_DEALLOCATE.match(sql))
435
+ forget_sql_prepared(deallocated[1])
436
+ end
437
+ end
438
+
439
+ # +DEALLOCATE ALL+ un-prepares every statement of the session, which +ALL+ in quotes does not: that
440
+ # names one statement actually called +ALL+.
441
+ def forget_sql_prepared(name_token)
442
+ if !name_token.start_with?('"') && name_token.casecmp('ALL').zero?
443
+ @prepared_on.clear
444
+ @prepared_sql.clear
445
+ else
446
+ forget_prepared([statement_name_of(name_token)], nil, nil)
447
+ end
448
+ end
449
+
450
+ # The name a statement prepared by a +PREPARE+ ends up with. Being an identifier, it is folded to
451
+ # lower case unless it was quoted, and that folded name is the one the +exec_prepared+ which runs
452
+ # it has to give as well, so it is the one to remember it under.
453
+ def statement_name_of(name_token)
454
+ return name_token.downcase unless name_token.start_with?('"')
455
+
456
+ name_token[1..-2].gsub('""', '"')
457
+ end
458
+
459
+ def remember_async(_args, _result, sql)
460
+ @async_conn = current_conn
461
+ @async_sql = sql
462
+ end
463
+
464
+ def forget_async(_args, _result, _sql)
465
+ @async_conn = nil
466
+ @async_sql = nil
467
+ end
468
+
469
+ # get_result answers nil once the last result of a pending exchange has been read, and there is
470
+ # nothing left to be bound to.
471
+ def forget_async_when_drained(_args, result, _sql)
472
+ return unless result.nil?
473
+
474
+ @async_conn = nil
475
+ @async_sql = nil
476
+ end
477
+
478
+ def forget_copy(_args, _result, _sql)
479
+ @copy_conn = nil
480
+ @copy_sql = nil
481
+ end
482
+
483
+ def remember_large_object(_args, _result, _sql)
484
+ @lo_conn = current_conn
485
+ end
486
+
487
+ def forget_large_object(_args, _result, _sql)
488
+ @lo_conn = nil
489
+ end
490
+
491
+ def ensure_pg!
492
+ require 'pg'
493
+ rescue LoadError
494
+ raise LoadError, "WrapperPgConnection requires 'pg'. Add it to your Gemfile: gem 'pg'"
495
+ end
496
+
497
+ def current_conn
498
+ @service_container.connection_service.current_connection
499
+ end
500
+
501
+ def pm
502
+ @service_container.plugin_manager
503
+ end
504
+
505
+ def driver_dialect
506
+ @service_container.dialect_service.driver_dialect
507
+ end
508
+
509
+ def network_bound_methods
510
+ @network_bound_methods ||= driver_dialect.network_bound_methods
511
+ end
512
+
513
+ def wrap_pg_result(result, sql = nil)
514
+ return result unless result.is_a?(PG::Result)
515
+
516
+ WrapperPgResult.new(result, @service_container, current_conn, sql)
517
+ end
518
+ end
519
+
520
+ class WrapperPgResult
521
+ include Enumerable
522
+
523
+ # @param sql [String, nil] the SQL that produced the result, kept so that plugins which
524
+ # inspect statements still see it when the rows are read
525
+ def initialize(result, service_container, connection, sql = nil)
526
+ @result = result
527
+ @service_container = service_container
528
+ @connection = connection
529
+ @sql = sql
530
+ end
531
+
532
+ def each(&)
533
+ pm.execute(RubyMethod::RESULT_EACH, current_conn, ->(&blk) { @result.each(&blk) },
534
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields }, &)
535
+ end
536
+
537
+ def each_row(&)
538
+ pm.execute(RubyMethod::RESULT_EACH_ROW, current_conn, ->(&blk) { @result.each_row(&blk) },
539
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields }, &)
540
+ end
541
+
542
+ def to_a
543
+ pm.execute(RubyMethod::RESULT_TO_A, current_conn, -> { @result.to_a },
544
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields })
545
+ end
546
+
547
+ def [](index)
548
+ pm.execute(RubyMethod::RESULT_BRACKET, current_conn, ->(*a) { @result[*a] }, index,
549
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields })
550
+ end
551
+
552
+ def values
553
+ pm.execute(RubyMethod::RESULT_VALUES, current_conn, -> { @result.values },
554
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields })
555
+ end
556
+
557
+ def column_values(index)
558
+ pm.execute(RubyMethod::RESULT_COLUMN_VALUES, current_conn, ->(*a) { @result.column_values(*a) }, index,
559
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields })
560
+ end
561
+
562
+ def field_values(field_name)
563
+ pm.execute(RubyMethod::RESULT_FIELD_VALUES, current_conn, ->(*a) { @result.field_values(*a) }, field_name,
564
+ bounded_conn: @connection, sql: @sql)
565
+ end
566
+
567
+ def tuple(index)
568
+ pm.execute(RubyMethod::RESULT_TUPLE, current_conn, ->(*a) { @result.tuple(*a) }, index,
569
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields })
570
+ end
571
+
572
+ def tuple_values(index)
573
+ pm.execute(RubyMethod::RESULT_TUPLE_VALUES, current_conn, ->(*a) { @result.tuple_values(*a) }, index,
574
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields })
575
+ end
576
+
577
+ def getvalue(row, column)
578
+ pm.execute(RubyMethod::RESULT_GETVALUE, current_conn, ->(*a) { @result.getvalue(*a) }, row, column,
579
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields })
580
+ end
581
+
582
+ # The single-row-mode iterators, which read rows off the wire one at a time rather than from a
583
+ # buffered result; they hand out the same row shapes as +each+, +each_row+ and +tuple+.
584
+ def stream_each(&)
585
+ pm.execute(RubyMethod::RESULT_STREAM_EACH, current_conn, ->(&blk) { @result.stream_each(&blk) },
586
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields }, &)
587
+ end
588
+
589
+ def stream_each_row(&)
590
+ pm.execute(RubyMethod::RESULT_STREAM_EACH_ROW, current_conn, ->(&blk) { @result.stream_each_row(&blk) },
591
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields }, &)
592
+ end
593
+
594
+ def stream_each_tuple(&)
595
+ pm.execute(RubyMethod::RESULT_STREAM_EACH_TUPLE, current_conn, ->(&blk) { @result.stream_each_tuple(&blk) },
596
+ bounded_conn: @connection, sql: @sql, field_names: -> { @result.fields }, &)
597
+ end
598
+
599
+ # Delegate non-network methods directly
600
+ def fields
601
+ @result.fields
602
+ end
603
+
604
+ def ntuples
605
+ @result.ntuples
606
+ end
607
+
608
+ def nfields
609
+ @result.nfields
610
+ end
611
+
612
+ def cmd_tuples
613
+ @result.cmd_tuples
614
+ end
615
+
616
+ def cmd_status
617
+ @result.cmd_status
618
+ end
619
+
620
+ def result_status
621
+ @result.result_status
622
+ end
623
+
624
+ def clear
625
+ @result.clear
626
+ end
627
+
628
+ alias num_tuples ntuples
629
+ alias count ntuples
630
+ alias size ntuples
631
+
632
+ def method_missing(method_name, *, &)
633
+ @result.send(method_name, *, &)
634
+ end
635
+
636
+ def respond_to_missing?(method, include_private = false)
637
+ @result.respond_to?(method, include_private) || super
638
+ end
639
+
640
+ def inspect
641
+ format('#<%<class>s:0x%<addr>016x>', class: self.class.name, addr: object_id << 1)
642
+ end
643
+ alias to_s inspect
644
+
645
+ def pretty_print(pp)
646
+ pp.text(inspect)
647
+ end
648
+
649
+ private
650
+
651
+ def current_conn
652
+ @service_container.connection_service.current_connection
653
+ end
654
+
655
+ def pm
656
+ @service_container.plugin_manager
657
+ end
658
+ end
659
+ end