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,154 @@
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
+ require_relative '../ruby_method'
18
+ require_relative '../logging'
19
+
20
+ module AwsAdvancedRubyDriverWrapper
21
+ module DriverDialects
22
+ module DriverDialect
23
+ include Logging
24
+
25
+ COMMON_NETWORK_BOUND_METHODS = Set[
26
+ RubyMethod::CONNECT.name,
27
+ RubyMethod::CONNECTION_CLOSE.name,
28
+ RubyMethod::CONNECTION_RESET.name,
29
+ RubyMethod::CONNECTION_PREPARE.name,
30
+ RubyMethod::STATEMENT_EXECUTE.name,
31
+ RubyMethod::STATEMENT_CLOSE.name
32
+ ].freeze
33
+
34
+ DEFAULT_MONITORING_TIMEOUT_SEC = 5
35
+
36
+ def connect(host_info, config)
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def execute(connection, sql)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def execute_with_params(connection, sql, params)
45
+ raise NotImplementedError
46
+ end
47
+
48
+ # Rewrites +?+ placeholders into the driver's native placeholder syntax.
49
+ # @param sql [String] SQL written with +?+ placeholders
50
+ # @return [String]
51
+ def translate_placeholders(sql)
52
+ raise NotImplementedError
53
+ end
54
+
55
+ # Wraps binary data as the bind value the driver needs for a bytea/blob parameter.
56
+ # @param bytes [String] binary data
57
+ # @return [Object] the driver-specific bind value
58
+ def binary_param(bytes)
59
+ raise NotImplementedError
60
+ end
61
+
62
+ # Reads a bytea/blob column value back into binary data.
63
+ # @param value [String] the raw column value
64
+ # @return [String] binary data
65
+ def read_binary(value)
66
+ raise NotImplementedError
67
+ end
68
+
69
+ # The number of rows an INSERT, UPDATE, or DELETE changed.
70
+ # @param connection [Object] the driver connection
71
+ # @param result [Object] the value the statement returned
72
+ # @return [Integer]
73
+ def affected_rows(connection, result)
74
+ raise NotImplementedError
75
+ end
76
+
77
+ # Runs an INSERT and returns the id it generated.
78
+ # @param sql [String] native SQL for the INSERT, without a RETURNING clause
79
+ # @param id_column [String] the generated column to return
80
+ # @return [Integer, nil]
81
+ def insert_returning_id(connection, sql, params, id_column)
82
+ raise NotImplementedError
83
+ end
84
+
85
+ # The trailing upsert clause for an INSERT, in the driver's own grammar.
86
+ # @param conflict_columns [Array<String>] the columns whose conflict triggers the update
87
+ # @param update_columns [Array<String>] the columns to overwrite from the incoming row
88
+ # @return [String]
89
+ def upsert_clause(conflict_columns, update_columns)
90
+ raise NotImplementedError
91
+ end
92
+
93
+ # The equality operator for the wrapper's own SQL, in the driver's own grammar.
94
+ # @return [String]
95
+ def equals_operator
96
+ raise NotImplementedError
97
+ end
98
+
99
+ # A query returning a table's foreign keys as rows with +from_column+, +to_table+, and
100
+ # +to_column+, using +?+ placeholders for the schema and table names.
101
+ # @return [String]
102
+ def foreign_key_query
103
+ raise NotImplementedError
104
+ end
105
+
106
+ def ping(connection)
107
+ raise NotImplementedError
108
+ end
109
+
110
+ def closed?(connection)
111
+ raise NotImplementedError
112
+ end
113
+
114
+ def close_connection(connection)
115
+ raise NotImplementedError
116
+ end
117
+
118
+ def sql_state(_exception)
119
+ nil
120
+ end
121
+
122
+ def network_bound_methods
123
+ COMMON_NETWORK_BOUND_METHODS
124
+ end
125
+
126
+ def prepare_connect_config(host_info, config)
127
+ raise NotImplementedError
128
+ end
129
+
130
+ # Returns the property key the underlying driver expects for the database username.
131
+ # Override in driver-specific dialects where the key differs.
132
+ def user_property_key
133
+ :user
134
+ end
135
+
136
+ # Applies default socket/connect timeouts to monitoring connection driver props.
137
+ # These ensure that a query or close on a dead connection raises a timeout error
138
+ # rather than hanging indefinitely or segfaulting.
139
+ # Implementations should only apply these properties if the user hasn't already set them.
140
+ # @param driver_props [Hash] the monitoring connection driver props (mutated in place)
141
+ def apply_monitoring_defaults(driver_props)
142
+ # No-op by default; driver-specific dialects override.
143
+ end
144
+
145
+ # Returns +true+/+false+ if the driver can report transaction state directly,
146
+ # or +nil+ if it cannot (SQL inference is used as fallback).
147
+ # @param connection [Object]
148
+ # @return [Boolean, nil]
149
+ def reported_in_transaction(_connection)
150
+ nil
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,55 @@
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
+ require_relative 'mysql_driver_dialect'
18
+ require_relative 'pg_driver_dialect'
19
+ require_relative '../errors'
20
+ require_relative '../errors/mysql_error_handler'
21
+ require_relative '../errors/pg_error_handler'
22
+
23
+ module AwsAdvancedRubyDriverWrapper
24
+ module DriverDialects
25
+ module DriverDialectManager
26
+ MYSQL_DIALECT = MysqlDriverDialect.new.freeze
27
+ PG_DIALECT = PgDriverDialect.new.freeze
28
+
29
+ REGISTRY = {
30
+ mysql2: {
31
+ driver_dialect: MYSQL_DIALECT,
32
+ error_handler: Errors::MysqlErrorHandler.new(MYSQL_DIALECT)
33
+ },
34
+ postgresql: {
35
+ driver_dialect: PG_DIALECT,
36
+ error_handler: Errors::PgErrorHandler.new(PG_DIALECT)
37
+ }
38
+ }.freeze
39
+
40
+ def self.get_dialect(driver_name)
41
+ fetch_entry(driver_name)[:driver_dialect]
42
+ end
43
+
44
+ def self.get_error_handler(driver_name)
45
+ fetch_entry(driver_name)[:error_handler]
46
+ end
47
+
48
+ def self.fetch_entry(driver_name)
49
+ REGISTRY.fetch(driver_name) do
50
+ raise Errors::AwsError, "Unknown driver: #{driver_name}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,165 @@
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
+ require_relative 'driver_dialect'
18
+ require_relative '../host/connection_string_host_list_provider'
19
+
20
+ module AwsAdvancedRubyDriverWrapper
21
+ module DriverDialects
22
+ class MysqlDriverDialect
23
+ include DriverDialect
24
+
25
+ # Every mysql2 call that talks to the server. A call that is not listed here is handed straight
26
+ # to the driver, bypassing the plugin pipeline.
27
+ #
28
+ # Not listed, because libmysql answers them without talking to the server: escape, the row and
29
+ # column counts, last_id, affected_rows, info, warning_count, thread_id, server_info,
30
+ # session_track, and the connection's own settings.
31
+ NETWORK_BOUND_METHODS = (COMMON_NETWORK_BOUND_METHODS | Set[
32
+ RubyMethod::CONNECTION_QUERY.name,
33
+ RubyMethod::CONNECTION_ASYNC_RESULT.name,
34
+ RubyMethod::CONNECTION_SELECT_DB.name,
35
+ RubyMethod::CONNECTION_MORE_RESULTS.name,
36
+ RubyMethod::CONNECTION_NEXT_RESULT.name,
37
+ RubyMethod::CONNECTION_STORE_RESULT.name,
38
+ RubyMethod::CONNECTION_ABANDON_RESULTS.name,
39
+ RubyMethod::CONNECTION_SET_SERVER_OPTION.name,
40
+ RubyMethod::RESULT_EACH.name,
41
+ RubyMethod::RESULT_FREE.name
42
+ ]).freeze
43
+
44
+ def connect(host_info, config)
45
+ ::Mysql2::Client.new(**prepare_connect_config(host_info, config))
46
+ end
47
+
48
+ def execute(connection, sql)
49
+ raise Mysql2::Error, 'MySQL client is not connected' if connection.nil? || connection.closed?
50
+
51
+ connection.query(sql)
52
+ end
53
+
54
+ def execute_with_params(connection, sql, params)
55
+ raise Mysql2::Error, 'MySQL client is not connected' if connection.nil? || connection.closed?
56
+
57
+ stmt = connection.prepare(sql)
58
+ result = stmt.execute(*params)
59
+ # Everything the caller needs must be read off the statement before the ensure closes its
60
+ # handle. A SELECT result is bound to the handle, so materialize its rows now - otherwise a
61
+ # later iteration (e.g. SqlRunner calling #to_a) raises "Statement handle already closed".
62
+ # A non-SELECT statement returns nil, and its affected-row count must come from the statement
63
+ # too: once the handle is closed the connection-level mysql_affected_rows returns its -1 error
64
+ # sentinel and mysql2 raises. So return that count for #affected_rows to hand back.
65
+ result.is_a?(::Mysql2::Result) ? result.to_a : stmt.affected_rows
66
+ ensure
67
+ stmt&.close
68
+ end
69
+
70
+ # mysql2 uses +?+ placeholders, so the SQL is already in its native form.
71
+ def translate_placeholders(sql)
72
+ sql
73
+ end
74
+
75
+ # mysql2 binds a blob parameter as raw binary bytes.
76
+ def binary_param(bytes)
77
+ bytes.b
78
+ end
79
+
80
+ # mysql2 hands back a blob column as a string that only needs its encoding forced to binary.
81
+ def read_binary(value)
82
+ value.b
83
+ end
84
+
85
+ # A prepared statement (execute_with_params) hands back its own affected-row count, since the
86
+ # connection-level count is unreadable once the statement handle is closed. A plain
87
+ # connection.query (execute) leaves nil, so the count is read off the connection instead.
88
+ def affected_rows(connection, result)
89
+ result.is_a?(Integer) ? result : connection.affected_rows.to_i
90
+ end
91
+
92
+ # mysql2 has no RETURNING clause, so the generated id is read from the connection afterwards.
93
+ def insert_returning_id(connection, sql, params, _id_column)
94
+ execute_with_params(connection, sql, params)
95
+ id = connection.last_id
96
+ id&.positive? ? id : nil
97
+ end
98
+
99
+ # mysql2 upserts with ON DUPLICATE KEY UPDATE, reading the incoming row from VALUES().
100
+ def upsert_clause(_conflict_columns, update_columns)
101
+ assignments = update_columns.map { |column| "#{column} = VALUES(#{column})" }.join(', ')
102
+ "ON DUPLICATE KEY UPDATE #{assignments}"
103
+ end
104
+
105
+ # MySQL has no user-defined operators, so the plain operator always means the built-in one.
106
+ def equals_operator
107
+ '='
108
+ end
109
+
110
+ def foreign_key_query
111
+ 'SELECT column_name AS from_column, referenced_table_name AS to_table, ' \
112
+ 'referenced_column_name AS to_column FROM information_schema.key_column_usage ' \
113
+ 'WHERE table_schema = ? AND table_name = ? AND referenced_table_name IS NOT NULL'
114
+ end
115
+
116
+ def ping(connection)
117
+ connection.ping
118
+ rescue StandardError
119
+ false
120
+ end
121
+
122
+ def closed?(connection)
123
+ connection.nil? || connection.closed?
124
+ end
125
+
126
+ def close_connection(connection)
127
+ return if connection.nil? || connection.closed?
128
+
129
+ connection.close
130
+ rescue StandardError => e
131
+ logger.error("Failed to close MySQL connection: #{e.message}")
132
+ end
133
+
134
+ def sql_state(exception)
135
+ return nil unless exception.respond_to?(:sql_state)
136
+
137
+ exception.sql_state
138
+ end
139
+
140
+ def network_bound_methods
141
+ NETWORK_BOUND_METHODS
142
+ end
143
+
144
+ def prepare_connect_config(host_info, config)
145
+ cfg = {}
146
+ config.each { |k, v| cfg[k] = v }
147
+ cfg[:host] = host_info.host if host_info.host_specified?
148
+ cfg[:port] = host_info.port.to_i if host_info.port_specified?
149
+ cfg
150
+ end
151
+
152
+ def user_property_key
153
+ :username
154
+ end
155
+
156
+ def apply_monitoring_defaults(driver_props)
157
+ # mysql2 read_timeout / write_timeout (in seconds) ensure that queries and closes
158
+ # on a dead socket raise Mysql2::Error::TimeoutError instead of segfaulting.
159
+ driver_props[:read_timeout] ||= DEFAULT_MONITORING_TIMEOUT_SEC
160
+ driver_props[:write_timeout] ||= DEFAULT_MONITORING_TIMEOUT_SEC
161
+ driver_props[:connect_timeout] ||= DEFAULT_MONITORING_TIMEOUT_SEC
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,201 @@
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
+ require_relative 'driver_dialect'
18
+
19
+ module AwsAdvancedRubyDriverWrapper
20
+ module DriverDialects
21
+ class PgDriverDialect
22
+ include DriverDialect
23
+
24
+ # Every pg call that talks to the server. A call that is not listed here is handed straight
25
+ # to the driver, bypassing the plugin pipeline.
26
+ #
27
+ # pg gives most of these operations several spellings (+query+ and +async_query+ for +exec+, an
28
+ # +async_+ and a +sync_+ form for many others, a short +lo*+ form for every large object call).
29
+ # One entry covers every spelling of a libpq operation: WrapperPgConnection recognizes which operation a
30
+ # spelling performs and enters the pipeline under that name, then asks the driver for the spelling
31
+ # it was given. Its OPERATIONS table has an entry per name listed here.
32
+ #
33
+ # Not listed, because libpq performs them without talking to the server: enter_pipeline_mode,
34
+ # exit_pipeline_mode, is_busy, setnonblocking, set_single_row_mode, set_chunked_rows_mode, the
35
+ # escaping and quoting calls, and the accessors for connection parameters and type maps.
36
+ NETWORK_BOUND_METHODS = (COMMON_NETWORK_BOUND_METHODS | Set[
37
+ RubyMethod::CONNECTION_EXEC.name,
38
+ RubyMethod::CONNECTION_ASYNC_EXEC.name,
39
+ RubyMethod::CONNECTION_EXEC_PARAMS.name,
40
+ RubyMethod::CONNECTION_EXEC_PREPARED.name,
41
+ RubyMethod::CONNECTION_DESCRIBE_PREPARED.name,
42
+ RubyMethod::CONNECTION_DESCRIBE_PORTAL.name,
43
+ RubyMethod::CONNECTION_TRANSACTION.name,
44
+ RubyMethod::CONNECTION_COPY_DATA.name,
45
+ RubyMethod::CONNECTION_PUT_COPY_DATA.name,
46
+ RubyMethod::CONNECTION_GET_COPY_DATA.name,
47
+ RubyMethod::CONNECTION_PUT_COPY_END.name,
48
+ RubyMethod::CONNECTION_SEND_QUERY.name,
49
+ RubyMethod::CONNECTION_SEND_QUERY_PARAMS.name,
50
+ RubyMethod::CONNECTION_SEND_QUERY_PREPARED.name,
51
+ RubyMethod::CONNECTION_SEND_PREPARE.name,
52
+ RubyMethod::CONNECTION_GET_RESULT.name,
53
+ RubyMethod::CONNECTION_GET_LAST_RESULT.name,
54
+ RubyMethod::CONNECTION_CANCEL.name,
55
+ RubyMethod::CONNECTION_SET_CLIENT_ENCODING.name,
56
+ RubyMethod::CONNECTION_WAIT_FOR_NOTIFY.name,
57
+ RubyMethod::CONNECTION_NOTIFIES.name,
58
+ RubyMethod::CONNECTION_CONSUME_INPUT.name,
59
+ RubyMethod::CONNECTION_FLUSH.name,
60
+ RubyMethod::CONNECTION_LO_OPEN.name,
61
+ RubyMethod::CONNECTION_LO_READ.name,
62
+ RubyMethod::CONNECTION_LO_WRITE.name,
63
+ RubyMethod::CONNECTION_LO_CLOSE.name,
64
+ RubyMethod::CONNECTION_CLOSE_PREPARED.name,
65
+ RubyMethod::CONNECTION_CLOSE_PORTAL.name,
66
+ RubyMethod::CONNECTION_DISCARD_RESULTS.name,
67
+ RubyMethod::CONNECTION_SEND_DESCRIBE_PREPARED.name,
68
+ RubyMethod::CONNECTION_SEND_DESCRIBE_PORTAL.name,
69
+ RubyMethod::CONNECTION_SEND_FLUSH_REQUEST.name,
70
+ RubyMethod::CONNECTION_PIPELINE_SYNC.name,
71
+ RubyMethod::CONNECTION_SEND_PIPELINE_SYNC.name,
72
+ RubyMethod::CONNECTION_BLOCK.name,
73
+ RubyMethod::CONNECTION_RESET_START.name,
74
+ RubyMethod::CONNECTION_RESET_POLL.name,
75
+ RubyMethod::CONNECTION_ENCRYPT_PASSWORD.name,
76
+ RubyMethod::CONNECTION_SET_DEFAULT_ENCODING.name,
77
+ RubyMethod::CONNECTION_INTERNAL_ENCODING_SET.name,
78
+ RubyMethod::CONNECTION_LO_CREAT.name,
79
+ RubyMethod::CONNECTION_LO_CREATE.name,
80
+ RubyMethod::CONNECTION_LO_IMPORT.name,
81
+ RubyMethod::CONNECTION_LO_EXPORT.name,
82
+ RubyMethod::CONNECTION_LO_UNLINK.name,
83
+ RubyMethod::CONNECTION_LO_LSEEK.name,
84
+ RubyMethod::CONNECTION_LO_TELL.name,
85
+ RubyMethod::CONNECTION_LO_TRUNCATE.name
86
+ ]).freeze
87
+
88
+ def connect(host_info, config)
89
+ ::PG::Connection.new(**prepare_connect_config(host_info, config))
90
+ end
91
+
92
+ def execute(connection, sql)
93
+ connection.exec(sql)
94
+ end
95
+
96
+ def execute_with_params(connection, sql, params)
97
+ connection.exec_params(sql, params)
98
+ end
99
+
100
+ # pg uses numbered +$1+, +$2+ ... placeholders.
101
+ def translate_placeholders(sql)
102
+ index = 0
103
+ sql.gsub('?') do
104
+ index += 1
105
+ "$#{index}"
106
+ end
107
+ end
108
+
109
+ # pg binds a bytea parameter as a value tagged with binary format 1.
110
+ def binary_param(bytes)
111
+ { value: bytes, format: 1 }
112
+ end
113
+
114
+ # pg hands back a bytea column in its hex (or older octal) text format.
115
+ def read_binary(value)
116
+ ::PG::Connection.unescape_bytea(value)
117
+ end
118
+
119
+ # pg reports the affected row count on the result.
120
+ def affected_rows(_connection, result)
121
+ result.respond_to?(:cmd_tuples) ? result.cmd_tuples.to_i : 0
122
+ end
123
+
124
+ # pg returns the generated id with a RETURNING clause.
125
+ def insert_returning_id(connection, sql, params, id_column)
126
+ row = execute_with_params(connection, "#{sql} RETURNING #{id_column}", params)&.first
127
+ row && row[id_column].to_i
128
+ end
129
+
130
+ # pg upserts with ON CONFLICT ... DO UPDATE, reading the incoming row from EXCLUDED.
131
+ def upsert_clause(conflict_columns, update_columns)
132
+ assignments = update_columns.map { |column| "#{column} = EXCLUDED.#{column}" }.join(', ')
133
+ "ON CONFLICT (#{conflict_columns.join(', ')}) DO UPDATE SET #{assignments}"
134
+ end
135
+
136
+ # pg resolves an unqualified operator through the search_path, where a user-defined operator
137
+ # with a closer type match would be chosen over the built-in one, so pin it to pg_catalog.
138
+ def equals_operator
139
+ 'OPERATOR(pg_catalog.=)'
140
+ end
141
+
142
+ def foreign_key_query
143
+ eq = equals_operator
144
+ 'SELECT kcu.column_name AS from_column, ccu.table_name AS to_table, ccu.column_name AS to_column ' \
145
+ 'FROM information_schema.table_constraints tc ' \
146
+ 'JOIN information_schema.key_column_usage kcu ' \
147
+ "ON tc.constraint_name #{eq} kcu.constraint_name AND tc.table_schema #{eq} kcu.table_schema " \
148
+ 'JOIN information_schema.constraint_column_usage ccu ' \
149
+ "ON tc.constraint_name #{eq} ccu.constraint_name AND tc.table_schema #{eq} ccu.table_schema " \
150
+ "WHERE tc.constraint_type #{eq} 'FOREIGN KEY' AND tc.table_schema #{eq} $1 AND tc.table_name #{eq} $2"
151
+ end
152
+
153
+ def closed?(connection)
154
+ connection.finished? || connection.status != ::PG::CONNECTION_OK
155
+ end
156
+
157
+ def close_connection(connection)
158
+ return if connection.nil? || connection.finished?
159
+
160
+ connection.close
161
+ rescue StandardError => e
162
+ logger.error("Failed to close PostgreSQL connection: #{e.message}")
163
+ end
164
+
165
+ def sql_state(exception)
166
+ return nil unless exception.is_a?(::PG::Error) && exception.result
167
+
168
+ exception.result.error_field(::PG::PG_DIAG_SQLSTATE)
169
+ end
170
+
171
+ def network_bound_methods
172
+ NETWORK_BOUND_METHODS
173
+ end
174
+
175
+ def prepare_connect_config(host_info, config)
176
+ cfg = {}
177
+ config.each { |k, v| cfg[k] = v }
178
+ cfg[:host] = host_info.host if host_info.host_specified?
179
+ cfg[:port] = host_info.port if host_info.port_specified?
180
+ cfg[:dbname] = cfg.delete(:database) if !cfg.key?(:dbname) && cfg.key?(:database)
181
+ cfg
182
+ end
183
+
184
+ def apply_monitoring_defaults(driver_props)
185
+ driver_props[:connect_timeout] ||= DEFAULT_MONITORING_TIMEOUT_SEC
186
+ end
187
+
188
+ # PG reads transaction state directly from the connection.
189
+ # Returns nil for PQTRANS_UNKNOWN (broken) and PQTRANS_ACTIVE (command in flight)
190
+ # so SessionStateService falls back to SQL inference.
191
+ def reported_in_transaction(connection)
192
+ case connection.transaction_status
193
+ when ::PG::PQTRANS_INTRANS, ::PG::PQTRANS_INERROR then true
194
+ when ::PG::PQTRANS_IDLE then false
195
+ end
196
+ rescue ::PG::ConnectionBad
197
+ false
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,62 @@
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
+ module AwsAdvancedRubyDriverWrapper
18
+ module Errors
19
+ module ErrorHandler
20
+ def network_error?(error)
21
+ check_cause_chain(error) { |sql_state, _| network_error_by_sql_state?(sql_state) }
22
+ end
23
+
24
+ def network_error_by_sql_state?(sql_state)
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def login_error?(error)
29
+ check_cause_chain(error) { |sql_state, _| login_error_by_sql_state?(sql_state) }
30
+ end
31
+
32
+ def login_error_by_sql_state?(sql_state)
33
+ raise NotImplementedError
34
+ end
35
+
36
+ def read_only_error?(error)
37
+ check_cause_chain(error) { |sql_state, _| read_only_error_by_sql_state?(sql_state) }
38
+ end
39
+
40
+ def read_only_error_by_sql_state?(sql_state, error_code = nil)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ private
45
+
46
+ def extract_sql_state(error)
47
+ @driver_dialect.sql_state(error)
48
+ end
49
+
50
+ def check_cause_chain(error)
51
+ current = error
52
+ while current
53
+ sql_state = extract_sql_state(current)
54
+ return true if yield(sql_state, current)
55
+
56
+ current = current.cause
57
+ end
58
+ false
59
+ end
60
+ end
61
+ end
62
+ end