yugabyte-ycql-driver 3.2.3.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 (145) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +13 -0
  3. data/README.md +242 -0
  4. data/ext/cassandra_murmur3/cassandra_murmur3.c +178 -0
  5. data/ext/cassandra_murmur3/extconf.rb +2 -0
  6. data/lib/cassandra/address_resolution.rb +36 -0
  7. data/lib/cassandra/address_resolution/policies.rb +2 -0
  8. data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
  9. data/lib/cassandra/address_resolution/policies/none.rb +35 -0
  10. data/lib/cassandra/aggregate.rb +123 -0
  11. data/lib/cassandra/argument.rb +51 -0
  12. data/lib/cassandra/attr_boolean.rb +33 -0
  13. data/lib/cassandra/auth.rb +100 -0
  14. data/lib/cassandra/auth/providers.rb +17 -0
  15. data/lib/cassandra/auth/providers/password.rb +65 -0
  16. data/lib/cassandra/cassandra_logger.rb +80 -0
  17. data/lib/cassandra/cluster.rb +331 -0
  18. data/lib/cassandra/cluster/client.rb +1612 -0
  19. data/lib/cassandra/cluster/connection_pool.rb +78 -0
  20. data/lib/cassandra/cluster/connector.rb +372 -0
  21. data/lib/cassandra/cluster/control_connection.rb +962 -0
  22. data/lib/cassandra/cluster/failed_connection.rb +35 -0
  23. data/lib/cassandra/cluster/metadata.rb +142 -0
  24. data/lib/cassandra/cluster/options.rb +145 -0
  25. data/lib/cassandra/cluster/registry.rb +284 -0
  26. data/lib/cassandra/cluster/schema.rb +405 -0
  27. data/lib/cassandra/cluster/schema/cql_type_parser.rb +112 -0
  28. data/lib/cassandra/cluster/schema/fetchers.rb +1627 -0
  29. data/lib/cassandra/cluster/schema/fqcn_type_parser.rb +175 -0
  30. data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
  31. data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +45 -0
  32. data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
  33. data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
  34. data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
  35. data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +102 -0
  36. data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
  37. data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
  38. data/lib/cassandra/column.rb +66 -0
  39. data/lib/cassandra/column_container.rb +326 -0
  40. data/lib/cassandra/compression.rb +69 -0
  41. data/lib/cassandra/compression/compressors/lz4.rb +73 -0
  42. data/lib/cassandra/compression/compressors/snappy.rb +69 -0
  43. data/lib/cassandra/custom_data.rb +53 -0
  44. data/lib/cassandra/driver.rb +260 -0
  45. data/lib/cassandra/errors.rb +784 -0
  46. data/lib/cassandra/execution/info.rb +69 -0
  47. data/lib/cassandra/execution/options.rb +267 -0
  48. data/lib/cassandra/execution/profile.rb +153 -0
  49. data/lib/cassandra/execution/profile_manager.rb +71 -0
  50. data/lib/cassandra/execution/trace.rb +192 -0
  51. data/lib/cassandra/executors.rb +113 -0
  52. data/lib/cassandra/function.rb +156 -0
  53. data/lib/cassandra/function_collection.rb +85 -0
  54. data/lib/cassandra/future.rb +794 -0
  55. data/lib/cassandra/host.rb +102 -0
  56. data/lib/cassandra/index.rb +118 -0
  57. data/lib/cassandra/keyspace.rb +473 -0
  58. data/lib/cassandra/listener.rb +87 -0
  59. data/lib/cassandra/load_balancing.rb +121 -0
  60. data/lib/cassandra/load_balancing/policies.rb +20 -0
  61. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +172 -0
  62. data/lib/cassandra/load_balancing/policies/round_robin.rb +141 -0
  63. data/lib/cassandra/load_balancing/policies/token_aware.rb +149 -0
  64. data/lib/cassandra/load_balancing/policies/white_list.rb +100 -0
  65. data/lib/cassandra/materialized_view.rb +92 -0
  66. data/lib/cassandra/null_logger.rb +56 -0
  67. data/lib/cassandra/protocol.rb +102 -0
  68. data/lib/cassandra/protocol/coder.rb +1085 -0
  69. data/lib/cassandra/protocol/cql_byte_buffer.rb +418 -0
  70. data/lib/cassandra/protocol/cql_protocol_handler.rb +448 -0
  71. data/lib/cassandra/protocol/request.rb +41 -0
  72. data/lib/cassandra/protocol/requests/auth_response_request.rb +51 -0
  73. data/lib/cassandra/protocol/requests/batch_request.rb +117 -0
  74. data/lib/cassandra/protocol/requests/credentials_request.rb +51 -0
  75. data/lib/cassandra/protocol/requests/execute_request.rb +122 -0
  76. data/lib/cassandra/protocol/requests/options_request.rb +39 -0
  77. data/lib/cassandra/protocol/requests/prepare_request.rb +59 -0
  78. data/lib/cassandra/protocol/requests/query_request.rb +112 -0
  79. data/lib/cassandra/protocol/requests/register_request.rb +38 -0
  80. data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
  81. data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
  82. data/lib/cassandra/protocol/response.rb +28 -0
  83. data/lib/cassandra/protocol/responses/already_exists_error_response.rb +50 -0
  84. data/lib/cassandra/protocol/responses/auth_challenge_response.rb +36 -0
  85. data/lib/cassandra/protocol/responses/auth_success_response.rb +36 -0
  86. data/lib/cassandra/protocol/responses/authenticate_response.rb +36 -0
  87. data/lib/cassandra/protocol/responses/error_response.rb +142 -0
  88. data/lib/cassandra/protocol/responses/event_response.rb +30 -0
  89. data/lib/cassandra/protocol/responses/function_failure_error_response.rb +52 -0
  90. data/lib/cassandra/protocol/responses/prepared_result_response.rb +62 -0
  91. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +59 -0
  92. data/lib/cassandra/protocol/responses/read_failure_error_response.rb +71 -0
  93. data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +61 -0
  94. data/lib/cassandra/protocol/responses/ready_response.rb +43 -0
  95. data/lib/cassandra/protocol/responses/result_response.rb +42 -0
  96. data/lib/cassandra/protocol/responses/rows_result_response.rb +39 -0
  97. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +73 -0
  98. data/lib/cassandra/protocol/responses/schema_change_result_response.rb +70 -0
  99. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +37 -0
  100. data/lib/cassandra/protocol/responses/status_change_event_response.rb +39 -0
  101. data/lib/cassandra/protocol/responses/supported_response.rb +36 -0
  102. data/lib/cassandra/protocol/responses/topology_change_event_response.rb +33 -0
  103. data/lib/cassandra/protocol/responses/unavailable_error_response.rb +58 -0
  104. data/lib/cassandra/protocol/responses/unprepared_error_response.rb +48 -0
  105. data/lib/cassandra/protocol/responses/void_result_response.rb +34 -0
  106. data/lib/cassandra/protocol/responses/write_failure_error_response.rb +73 -0
  107. data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +63 -0
  108. data/lib/cassandra/protocol/v1.rb +326 -0
  109. data/lib/cassandra/protocol/v3.rb +358 -0
  110. data/lib/cassandra/protocol/v4.rb +478 -0
  111. data/lib/cassandra/reconnection.rb +49 -0
  112. data/lib/cassandra/reconnection/policies.rb +20 -0
  113. data/lib/cassandra/reconnection/policies/constant.rb +46 -0
  114. data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
  115. data/lib/cassandra/result.rb +276 -0
  116. data/lib/cassandra/retry.rb +154 -0
  117. data/lib/cassandra/retry/policies.rb +21 -0
  118. data/lib/cassandra/retry/policies/default.rb +53 -0
  119. data/lib/cassandra/retry/policies/downgrading_consistency.rb +73 -0
  120. data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
  121. data/lib/cassandra/session.rb +270 -0
  122. data/lib/cassandra/statement.rb +32 -0
  123. data/lib/cassandra/statements.rb +23 -0
  124. data/lib/cassandra/statements/batch.rb +146 -0
  125. data/lib/cassandra/statements/bound.rb +65 -0
  126. data/lib/cassandra/statements/prepared.rb +235 -0
  127. data/lib/cassandra/statements/simple.rb +118 -0
  128. data/lib/cassandra/statements/void.rb +38 -0
  129. data/lib/cassandra/table.rb +240 -0
  130. data/lib/cassandra/time.rb +103 -0
  131. data/lib/cassandra/time_uuid.rb +78 -0
  132. data/lib/cassandra/timestamp_generator.rb +37 -0
  133. data/lib/cassandra/timestamp_generator/simple.rb +38 -0
  134. data/lib/cassandra/timestamp_generator/ticking_on_duplicate.rb +58 -0
  135. data/lib/cassandra/trigger.rb +67 -0
  136. data/lib/cassandra/tuple.rb +131 -0
  137. data/lib/cassandra/types.rb +1704 -0
  138. data/lib/cassandra/udt.rb +443 -0
  139. data/lib/cassandra/util.rb +464 -0
  140. data/lib/cassandra/uuid.rb +110 -0
  141. data/lib/cassandra/uuid/generator.rb +212 -0
  142. data/lib/cassandra/version.rb +21 -0
  143. data/lib/datastax/cassandra.rb +47 -0
  144. data/lib/ycql.rb +842 -0
  145. metadata +243 -0
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ # Represents a UUID value.
21
+ #
22
+ # This is a very basic implementation of UUIDs and exists more or less just
23
+ # to encode and decode UUIDs from and to Cassandra.
24
+ #
25
+ # If you want to generate UUIDs see {Cassandra::Uuid::Generator}.
26
+ #
27
+ class Uuid
28
+ # @private
29
+ RAW_FORMAT = '%032x'.force_encoding(Encoding::ASCII).freeze
30
+ # @private
31
+ HYPHEN = '-'.force_encoding(Encoding::ASCII).freeze
32
+ # @private
33
+ EMPTY_STRING = ''.freeze
34
+
35
+ # Creates a new UUID either from a string (expected to be on the standard 8-4-4-4-12
36
+ # form, or just 32 characters without hyphens), or from a 128 bit number.
37
+ #
38
+ # @param uuid [String] a 32 char uuid
39
+ #
40
+ # @raise [ArgumentError] if the string does not conform to the expected format
41
+ #
42
+ def initialize(uuid)
43
+ @n = case uuid
44
+ when String
45
+ from_s(uuid)
46
+ else
47
+ uuid
48
+ end
49
+ end
50
+
51
+ # Returns a string representation of this UUID in the standard 8-4-4-4-12 form.
52
+ #
53
+ def to_s
54
+ @s ||= begin
55
+ s = RAW_FORMAT % @n
56
+ s.insert(20, HYPHEN)
57
+ s.insert(16, HYPHEN)
58
+ s.insert(12, HYPHEN)
59
+ s.insert(8, HYPHEN)
60
+ s
61
+ end
62
+ end
63
+
64
+ # @private
65
+ def hash
66
+ @h ||= begin
67
+ h = 17
68
+ h = 31 * h + @n.hash
69
+ h
70
+ end
71
+ end
72
+
73
+ # Returns the numerical representation of this UUID
74
+ #
75
+ # @return [Bignum] the 128 bit numerical representation
76
+ #
77
+ def value
78
+ @n
79
+ end
80
+ alias to_i value
81
+
82
+ # @private
83
+ def eql?(other)
84
+ other.respond_to?(:value) && value == other.value
85
+ end
86
+ alias == eql?
87
+
88
+ if RUBY_ENGINE == 'jruby'
89
+ # @private
90
+ HEX_RE = /^[A-Fa-f0-9]+$/
91
+ # See https://github.com/jruby/jruby/issues/1608
92
+ # @private
93
+ def from_s(str)
94
+ str = str.gsub(HYPHEN, EMPTY_STRING)
95
+ raise ::ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
96
+ raise ::ArgumentError, "invalid value for Integer(): \"#{str}\"" unless str =~ HEX_RE
97
+ Integer(str, 16)
98
+ end
99
+ else
100
+ # @private
101
+ def from_s(str)
102
+ str = str.gsub(HYPHEN, EMPTY_STRING)
103
+ raise ::ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
104
+ Integer(str, 16)
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ require 'cassandra/uuid/generator'
@@ -0,0 +1,212 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ class Uuid
21
+ # A UUID generator.
22
+ #
23
+ # This class can be used to genereate Apache Cassandra timeuuid and uuid
24
+ # values.
25
+ #
26
+ # @see Cassandra::Uuid::Generator#now Generating a sequence time UUIDs
27
+ # with reasonable uniqueness guarantees.
28
+ # @see Cassandra::Uuid::Generator#at Generating a time UUID for a given
29
+ # time object or unix timestamp.
30
+ # @see Cassandra::Uuid::Generator#uuid Generating completely random v4
31
+ # UUIDs.
32
+ #
33
+ # @note Instances of this class are absolutely not threadsafe. You should
34
+ # never share instances between threads.
35
+ #
36
+ class Generator
37
+ # Create a new UUID generator.
38
+ #
39
+ # The clock ID and node ID components are set to random numbers when the
40
+ # generator is created. These are used for generation of time UUIDs only.
41
+ #
42
+ # @param [Integer] node_id an alternate node ID
43
+ # @param [Integer] clock_id an alternate clock ID
44
+ # @param [Object<#now>] clock used to generate timeuuid from current time
45
+ #
46
+ # @raise [ArgumentError] if clock doesn't respond to `now`
47
+ def initialize(node_id = (::SecureRandom.random_number(2**47) | 0x010000000000),
48
+ clock_id = ::SecureRandom.random_number(65536),
49
+ clock = ::Time)
50
+ raise ::ArgumentError, 'invalid clock' unless clock.respond_to?(:now)
51
+
52
+ @node_id = Integer(node_id)
53
+ @clock_id = Integer(clock_id)
54
+ @clock = clock
55
+ @last_usecs = nil
56
+ end
57
+
58
+ # Returns a new UUID with a time component that is the current time.
59
+ #
60
+ # If two calls to {#now} happen within the time afforded by the system
61
+ # clock resolution a counter is incremented and added to the time
62
+ # component.
63
+ #
64
+ # If the clock moves backwards the clock ID is reset to a new random
65
+ # number.
66
+ #
67
+ # @example Creating a sequential TimeUuids for the current time
68
+ # generator = Cassandra::Uuid::Generator.new
69
+ # timeuuids = 5.times.map { generator.now }
70
+ #
71
+ # puts timeuuids.zip(timeuuids.map(&:to_time)).map(&:inspect)
72
+ #
73
+ # # Outputs:
74
+ # # [8614b7d0-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
75
+ # # [8614b91a-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
76
+ # # [8614b960-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
77
+ # # [8614b99c-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
78
+ # # [8614b9ce-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
79
+ #
80
+ # @see Time.now
81
+ #
82
+ # @return [Cassandra::TimeUuid] a new UUID
83
+ def now
84
+ now = @clock.now
85
+ usecs = now.to_i * 1_000_000 + now.usec
86
+
87
+ if @last_usecs && @last_usecs - @sequence <= usecs && usecs <= @last_usecs
88
+ @sequence += 1
89
+ elsif @last_usecs && @last_usecs > usecs
90
+ @sequence = 0
91
+ @clock_id = ::SecureRandom.random_number(65536)
92
+ else
93
+ @sequence = 0
94
+ end
95
+
96
+ @last_usecs = usecs + @sequence
97
+ from_usecs(@last_usecs)
98
+ end
99
+
100
+ # Returns a new UUID with a time component based on the specified Time.
101
+ # A piece of jitter is added to ensure that multiple calls with the same
102
+ # time do not generate the same UUID (if you want determinism you can set
103
+ # the second parameter to zero).
104
+ #
105
+ # @overload at(time, jitter = SecureRandom.random_number(65536))
106
+ # @param [Time] time a Time instance
107
+ # @param [Integer] jitter a number of microseconds to add to the time
108
+ # @return [Cassandra::TimeUuid] a new UUID
109
+ # @overload at(seconds_with_frac, jitter = SecureRandom.random_number(65536))
110
+ # @param [Numeric] seconds_with_frac can be {Integer}, {Float},
111
+ # {Rational}, or other {Numeric}
112
+ # @param [Integer] jitter a number of microseconds to add to the time
113
+ # @return [Cassandra::TimeUuid] a new UUID
114
+ # @overload at(seconds, microseconds_with_frac, jitter = SecureRandom.random_number(65536))
115
+ # @param [Integer] seconds
116
+ # @param [Numeric] microseconds_with_frac can be {Integer}, {Float},
117
+ # {Rational}, or other {Numeric}
118
+ # @param [Integer] jitter a number of microseconds to add to the time
119
+ # @return [Cassandra::TimeUuid] a new UUID
120
+ #
121
+ # @note the `jitter` argument accepted by all variants of this method is
122
+ # required to add randomness to generated {Cassandra::TimeUuid} and
123
+ # might affect the order of generated timestamps. You should set
124
+ # `jitter` to 0 when the source time(stamp)s are unique.
125
+ #
126
+ # @example Creating a TimeUuid from a Time instance
127
+ # generator = Cassandra::Uuid::Generator.new
128
+ # timeuuid = generator.at(Time.at(1413582460))
129
+ #
130
+ # puts timeuuid.to_time
131
+ #
132
+ # # Outputs:
133
+ # # 2014-10-17 21:47:40 UTC
134
+ #
135
+ # @example Creating a TimeUuid from a timestamp
136
+ # generator = Cassandra::Uuid::Generator.new
137
+ # timeuuid = generator.at(1413582423)
138
+ #
139
+ # puts timeuuid.to_time
140
+ #
141
+ # # Outputs:
142
+ # # 2014-10-17 21:47:03 UTC
143
+ #
144
+ # @example Avoid jitter in generated TimeUuid
145
+ # timestamp = 1413582418
146
+ # generator = Cassandra::Uuid::Generator.new
147
+ # timeuuid = generator.at(timestamp, 0)
148
+ #
149
+ # puts timeuuid.to_time.to_i
150
+ #
151
+ # # Outputs:
152
+ # # 1413582418
153
+ #
154
+ # @raise [ArgumentError] when given no arguments or more than 3 arguments
155
+ #
156
+ # @see Time.at
157
+ def at(*args)
158
+ raise ::ArgumentError, 'not enough arguments' if args.empty?
159
+ raise ::ArgumentError, 'too many arguments' if args.size > 3
160
+
161
+ if args.first.is_a?(::Time)
162
+ time = args.shift
163
+ jitter = args.empty? ? ::SecureRandom.random_number(65536) : Integer(args.shift)
164
+ else
165
+ jitter = args.size > 2 ? Integer(args.pop) : ::SecureRandom.random_number(65536)
166
+ time = ::Time.at(*args)
167
+ end
168
+
169
+ from_usecs(time.to_i * 1_000_000 + time.usec + jitter)
170
+ end
171
+
172
+ # Returns a completely random version 4 UUID.
173
+ #
174
+ # @example Generating a random Uuid
175
+ # generator = Cassandra::Uuid::Generator.new
176
+ # uuid = generator.uuid
177
+ #
178
+ # puts uuid
179
+ #
180
+ # # Outputs:
181
+ # # 664dedae-e162-4bc0-9066-b9f1968252aa
182
+ #
183
+ # @see SecureRandom.uuid
184
+ #
185
+ # @return [Cassandra::Uuid] a new UUID
186
+ def uuid
187
+ Uuid.new(::SecureRandom.uuid)
188
+ end
189
+
190
+ private
191
+
192
+ # @private
193
+ def from_usecs(usecs)
194
+ t = TimeUuid::GREGORIAN_OFFSET + usecs * 10
195
+ time_hi = t & 0x0fff000000000000
196
+ time_mid = t & 0x0000ffff00000000
197
+ time_low = t & 0x00000000ffffffff
198
+ version = 1
199
+ clock_id = @clock_id & 0x3fff
200
+ node_id = @node_id & 0xffffffffffff
201
+ variant = 0x8000
202
+
203
+ n = (time_low << 96) | (time_mid << 48) | (time_hi << 16)
204
+ n |= version << 76
205
+ n |= (clock_id | variant) << 48
206
+ n |= node_id
207
+
208
+ TimeUuid.new(n)
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ VERSION = '3.2.3.1'.freeze
21
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module DataStax
20
+ @base = __FILE__ + '/../..'
21
+
22
+ def self.require(path)
23
+ if path.start_with?('cassandra/')
24
+ include(path)
25
+ else
26
+ ::Kernel.require(path)
27
+ end
28
+ end
29
+
30
+ def self.include(path)
31
+ path = File.expand_path(path + '.rb', @base)
32
+ class_eval(File.read(path), path, 1)
33
+ end
34
+
35
+ previous = nil
36
+ murmur3 = nil
37
+ if defined?(::Cassandra)
38
+ previous = ::Cassandra
39
+ murmur3 = ::Cassandra::Murmur3
40
+ Object.send(:remove_const, :Cassandra)
41
+ end
42
+ include 'cassandra'
43
+ murmur3 ||= ::Cassandra::Murmur3
44
+ DataStax::Cassandra::Murmur3 = murmur3
45
+ Object.send(:remove_const, :Cassandra) if defined?(::Cassandra)
46
+ ::Cassandra = previous if previous
47
+ end
data/lib/ycql.rb ADDED
@@ -0,0 +1,842 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ require 'ione'
20
+ require 'json'
21
+
22
+ require 'monitor'
23
+ require 'ipaddr'
24
+ require 'set'
25
+ require 'bigdecimal'
26
+ require 'forwardable'
27
+ require 'digest'
28
+ require 'stringio'
29
+ require 'resolv'
30
+ require 'openssl'
31
+ require 'securerandom'
32
+ require 'time'
33
+ require 'date'
34
+
35
+ module Cassandra
36
+ # A list of all supported request consistencies
37
+ # @see http://www.datastax.com/documentation/cassandra/2.0/cassandra/dml/dml_config_consistency_c.html Consistency
38
+ # levels in Apache Cassandra 2.0
39
+ # @see http://www.datastax.com/documentation/cassandra/1.2/cassandra/dml/dml_config_consistency_c.html Consistency
40
+ # levels in Apache Cassandra 1.2
41
+ # @see Cassandra::Session#execute_async
42
+ CONSISTENCIES = [:any, :one, :two, :three, :quorum, :all, :local_quorum,
43
+ :each_quorum, :serial, :local_serial, :local_one].freeze
44
+
45
+ # A list of all supported serial consistencies
46
+ # @see Cassandra::Session#execute_async
47
+ SERIAL_CONSISTENCIES = [:serial, :local_serial].freeze
48
+
49
+ # A list of all possible write types that a
50
+ # {Cassandra::Errors::WriteTimeoutError} can have.
51
+ #
52
+ # @see https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v2.spec#L872-L887 Description of
53
+ # possible types of writes in Apache Cassandra native protocol spec v1
54
+ WRITE_TYPES = [:simple, :batch, :unlogged_batch, :counter, :batch_log].freeze
55
+
56
+ CLUSTER_OPTIONS = [
57
+ :address_resolution,
58
+ :address_resolution_policy,
59
+ :allow_beta_protocol,
60
+ :auth_provider,
61
+ :client_cert,
62
+ :client_timestamps,
63
+ :compression,
64
+ :compressor,
65
+ :connect_timeout,
66
+ :connections_per_local_node,
67
+ :connections_per_remote_node,
68
+ :consistency,
69
+ :credentials,
70
+ :custom_types,
71
+ :datacenter,
72
+ :execution_profiles,
73
+ :futures_factory,
74
+ :heartbeat_interval,
75
+ :hosts,
76
+ :idle_timeout,
77
+ :listeners,
78
+ :load_balancing_policy,
79
+ :logger,
80
+ :nodelay,
81
+ :reconnection_policy,
82
+ :retry_policy,
83
+ :page_size,
84
+ :passphrase,
85
+ :password,
86
+ :port,
87
+ :private_key,
88
+ :protocol_version,
89
+ :requests_per_connection,
90
+ :schema_refresh_delay,
91
+ :schema_refresh_timeout,
92
+ :server_cert,
93
+ :shuffle_replicas,
94
+ :ssl,
95
+ :synchronize_schema,
96
+ :timeout,
97
+ :trace,
98
+ :username
99
+ ].freeze
100
+
101
+ # Creates a {Cassandra::Cluster Cluster instance}.
102
+ #
103
+ # @option options [Array<String, IPAddr>] :hosts (['127.0.0.1']) a list of
104
+ # initial addresses. Note that the entire list of cluster members will be
105
+ # discovered automatically once a connection to any hosts from the original
106
+ # list is successful.
107
+ #
108
+ # @option options [Integer] :port (9042) cassandra native protocol port.
109
+ #
110
+ # @option options [Boolean] :nodelay (true) when set to `true`, disables
111
+ # nagle algorithm.
112
+ #
113
+ # @option options [String] :datacenter (nil) name of current datacenter.
114
+ # First datacenter found will be assumed current by default. Note that you
115
+ # can skip this option if you specify only hosts from the local datacenter
116
+ # in `:hosts` option.
117
+ #
118
+ # @option options [Boolean] :shuffle_replicas (true) whether replicas list
119
+ # found by the default Token-Aware Load Balancing Policy should be
120
+ # shuffled. See {Cassandra::LoadBalancing::Policies::TokenAware#initialize Token-Aware Load Balancing Policy}.
121
+ #
122
+ # @option options [Hash<String|Symbol, ExecutionProfile>] :execution_profiles (nil)
123
+ # Hash of {Cassandra::Execution::Profile}s that are available for client use (e.g.
124
+ # {Session#execute}, {Session#execute_async}, {Session#prepare}, and {Session#prepare_async}).
125
+ #
126
+ # @option options [Numeric] :connect_timeout (10) connection timeout in
127
+ # seconds. Setting value to `nil` will reset it to 5 seconds.
128
+ #
129
+ # @option options [Numeric] :timeout (12) request execution timeout in
130
+ # seconds. Setting value to `nil` will remove request timeout.
131
+ #
132
+ # @option options [Numeric] :heartbeat_interval (30) how often should a
133
+ # heartbeat be sent to determine if a connection is alive. Several things to
134
+ # note about this option. Only one heartbeat request will ever be
135
+ # outstanding on a given connection. Each heatbeat will be sent in at least
136
+ # `:heartbeat_interval` seconds after the last request has been sent on a
137
+ # given connection. Setting value to `nil` will remove connection timeout.
138
+ #
139
+ # @option options [Numeric] :idle_timeout (60) period of inactivity after
140
+ # which a connection is considered dead. Note that this value should be at
141
+ # least a few times larger than `:heartbeat_interval`. Setting value to
142
+ # `nil` will remove automatic connection termination.
143
+ #
144
+ # @option options [String] :username (none) username to use for
145
+ # authentication to cassandra. Note that you must also specify `:password`.
146
+ #
147
+ # @option options [String] :password (none) password to use for
148
+ # authentication to cassandra. Note that you must also specify `:username`.
149
+ #
150
+ # @option options [Boolean, OpenSSL::SSL::SSLContext] :ssl (false) enable
151
+ # default ssl authentication if `true` (not recommended). Also accepts an
152
+ # initialized {OpenSSL::SSL::SSLContext}. Note that this option should be
153
+ # ignored if `:server_cert`, `:client_cert`, `:private_key` or
154
+ # `:passphrase` are given.
155
+ #
156
+ # @option options [String] :server_cert (none) path to server certificate or
157
+ # certificate authority file.
158
+ #
159
+ # @option options [String] :client_cert (none) path to client certificate
160
+ # file. Note that this option is only required when encryption is
161
+ # configured to require client authentication.
162
+ #
163
+ # @option options [String] :private_key (none) path to client private key.
164
+ # Note that this option is only required when encryption is configured to
165
+ # require client authentication.
166
+ #
167
+ # @option options [String] :passphrase (none) passphrase for private key.
168
+ #
169
+ # @option options [Symbol] :compression (none) compression to use. Must be
170
+ # either `:snappy` or `:lz4`. Also note, that in order for compression to
171
+ # work, you must install 'snappy' or 'lz4-ruby' gems.
172
+ #
173
+ # @option options [Cassandra::LoadBalancing::Policy] :load_balancing_policy
174
+ # default: token aware data center aware round robin.
175
+ #
176
+ # @option options [Symbol] :address_resolution (:none) a pre-configured
177
+ # address resolver to use. Must be one of `:none` or
178
+ # `:ec2_multi_region`.
179
+ #
180
+ # @option options [Integer] :connections_per_local_node (nil) Number of connections to
181
+ # open to each local node; the value of this option directly correlates to the number
182
+ # of requests the client can make to the local node concurrently. When `nil`, the
183
+ # setting is `1` for nodes that use the v3 or later protocol, and `2` for nodes that
184
+ # use the v2 or earlier protocol.
185
+ #
186
+ # @option options [Integer] :connections_per_remote_node (1) Number of connections to
187
+ # open to each remote node; the value of this option directly correlates to the
188
+ # number of requests the client can make to the remote node concurrently.
189
+ #
190
+ # @option options [Integer] :requests_per_connection (nil) Number of outstanding
191
+ # requests to support on one connection. Depending on the types of requests, some may
192
+ # get processed in parallel in the Cassandra node. When `nil`, the setting is `1024`
193
+ # for nodes that use the v3 or later protocol, and `128` for nodes that use the
194
+ # v2 or earlier protocol.
195
+ #
196
+ # @option options [Integer] :protocol_version (nil) Version of protocol to speak to
197
+ # nodes. By default, this is auto-negotiated to the highest common protocol version
198
+ # that all nodes in `:hosts` speak.
199
+ #
200
+ # @option options [Boolean] :allow_beta_protocol (false) whether the driver should attempt to speak to nodes
201
+ # with a beta version of the newest protocol (which is still under development). USE WITH CAUTION!
202
+ #
203
+ # @option options [Boolean, Cassandra::TimestampGenerator] :client_timestamps (false) whether the driver
204
+ # should send timestamps for each executed statement and possibly which timestamp generator to use. Enabling this
205
+ # setting helps mitigate Cassandra cluster clock skew because the timestamp of the client machine will be used.
206
+ # This does not help mitigate application cluster clock skew. Also accepts an initialized
207
+ # {Cassandra::TimestampGenerator}, `:simple` (indicating an instance of {Cassandra::TimestampGenerator::Simple}),
208
+ # or `:monotonic` (indicating an instance of {Cassandra::TimestampGenerator::TickingOnDuplicate}). If set to true,
209
+ # it defaults to {Cassandra::TimestampGenerator::Simple} for all Ruby flavors except JRuby. On JRuby, it defaults to
210
+ # {Cassandra::TimestampGenerator::TickingOnDuplicate}.
211
+ #
212
+ # @option options [Boolean] :synchronize_schema (true) whether the driver
213
+ # should automatically keep schema metadata synchronized. When enabled, the
214
+ # driver updates schema metadata after receiving schema change
215
+ # notifications from Cassandra. Setting this setting to `false` disables
216
+ # automatic schema updates. Schema metadata is used by the driver to
217
+ # determine cluster partitioners as well as to find partition keys and
218
+ # replicas of prepared statements, this information makes token aware load
219
+ # balancing possible. One can still
220
+ # {Cassandra::Cluster#refresh_schema refresh schema manually}.
221
+ #
222
+ # @option options [Numeric] :schema_refresh_delay (1) the driver will wait
223
+ # for `:schema_refresh_delay` before fetching metadata after receiving a
224
+ # schema change event. This timer is restarted every time a new schema
225
+ # change event is received. Finally, when the timer expires or a maximum
226
+ # wait time of `:schema_refresh_timeout` has been reached, a schema refresh
227
+ # attempt will be made and the timeout is reset.
228
+ #
229
+ # @option options [Numeric] :schema_refresh_timeout (10) the maximum delay
230
+ # before automatically refreshing schema. Such delay can occur whenever
231
+ # multiple schema change events are continuously arriving within
232
+ # `:schema_refresh_delay` interval.
233
+ #
234
+ # @option options [Cassandra::Reconnection::Policy] :reconnection_policy
235
+ # default: {Cassandra::Reconnection::Policies::Exponential Exponential}.
236
+ # Note that the default policy is configured with `(0.5, 30, 2)`.
237
+ #
238
+ # @option options [Cassandra::Retry::Policy] :retry_policy default:
239
+ # {Cassandra::Retry::Policies::Default Default Retry Policy}.
240
+ #
241
+ # @option options [Logger] :logger (none) logger. a {Logger} instance from the
242
+ # standard library or any object responding to standard log methods
243
+ # (`#debug`, `#info`, `#warn`, `#error` and `#fatal`).
244
+ #
245
+ # @option options [Enumerable<Cassandra::Listener>] :listeners (none)
246
+ # initial listeners. A list of initial cluster state listeners. Note that a
247
+ # `:load_balancing` policy is automatically registered with the cluster.
248
+ #
249
+ # @option options [Symbol] :consistency (:local_quorum) default consistency
250
+ # to use for all requests. Must be one of {Cassandra::CONSISTENCIES}.
251
+ #
252
+ # @option options [Boolean] :trace (false) whether or not to trace all
253
+ # requests by default.
254
+ #
255
+ # @option options [Integer] :page_size (10000) default page size for all
256
+ # select queries. Set this value to `nil` to disable paging.
257
+ #
258
+ # @option options [Hash{String => String}] :credentials (none) a hash of credentials -
259
+ # to be used with [credentials authentication in cassandra 1.2](https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v1.spec#L238-L250).
260
+ # Note that if you specified `:username` and `:password` options, those credentials
261
+ # are configured automatically.
262
+ #
263
+ # @option options [Cassandra::Auth::Provider] :auth_provider (none) a custom auth
264
+ # provider to be used with [SASL authentication in cassandra 2.0](https://github.com/apache/cassandra/blob/cassandra-2.0.16/doc/native_protocol_v2.spec#L257-L273).
265
+ # Note that if you have specified `:username` and `:password`, then a
266
+ # {Cassandra::Auth::Providers::Password Password Provider} will be used automatically.
267
+ #
268
+ # @option options [Cassandra::Compression::Compressor] :compressor (none) a
269
+ # custom compressor. Note that if you have specified `:compression`, an
270
+ # appropriate compressor will be provided automatically.
271
+ #
272
+ # @option options [Cassandra::AddressResolution::Policy]
273
+ # :address_resolution_policy default:
274
+ # {Cassandra::AddressResolution::Policies::None No Resolution Policy} a custom address
275
+ # resolution policy. Note that if you have specified `:address_resolution`, an
276
+ # appropriate address resolution policy will be provided automatically.
277
+ #
278
+ # @option options [Object<#all, #error, #value, #promise>] :futures_factory
279
+ # default: {Cassandra::Future} a futures factory to assist with integration
280
+ # into existing futures library. Note that promises returned by this object
281
+ # must conform to {Cassandra::Promise} api, which is not yet public. Things
282
+ # may change, use at your own risk.
283
+ #
284
+ # @example Connecting to localhost
285
+ # cluster = Cassandra.cluster
286
+ #
287
+ # @example Configuring {Cassandra::Cluster}
288
+ # cluster = Cassandra.cluster(
289
+ # username: username,
290
+ # password: password,
291
+ # hosts: ['10.0.1.1', '10.0.1.2', '10.0.1.3']
292
+ # )
293
+ #
294
+ # @return [Cassandra::Cluster] a cluster instance
295
+ def self.cluster(options = {})
296
+ cluster_async(options).get
297
+ end
298
+
299
+ # Creates a {Cassandra::Cluster Cluster instance}.
300
+ #
301
+ # @see Cassandra.cluster
302
+ #
303
+ # @return [Cassandra::Future<Cassandra::Cluster>] a future resolving to the
304
+ # cluster instance.
305
+ def self.cluster_async(options = {})
306
+ options, hosts = validate_and_massage_options(options)
307
+ rescue => e
308
+ futures = options.fetch(:futures_factory) { return Future::Error.new(e) }
309
+ futures.error(e)
310
+ else
311
+ driver = Driver.new(options)
312
+ driver.connect(hosts)
313
+ end
314
+
315
+ # @private
316
+ SSL_CLASSES = [::TrueClass, ::FalseClass, ::OpenSSL::SSL::SSLContext].freeze
317
+
318
+ # rubocop:disable Metrics/AbcSize
319
+ # rubocop:disable Metrics/CyclomaticComplexity
320
+ # rubocop:disable Metrics/PerceivedComplexity
321
+ # @private
322
+ def self.validate_and_massage_options(options)
323
+ options = options.select do |key, _|
324
+ CLUSTER_OPTIONS.include?(key)
325
+ end
326
+
327
+ if options.key?(:execution_profiles)
328
+ [:load_balancing_policy, :retry_policy, :timeout, :consistency].each do |opt|
329
+ raise ::ArgumentError, "#{opt} is not allowed when execution profiles are used" if options.key?(opt)
330
+ end
331
+ end
332
+
333
+ has_username = options.key?(:username)
334
+ has_password = options.key?(:password)
335
+ if has_username || has_password
336
+ if has_username && !has_password
337
+ raise ::ArgumentError,
338
+ 'both :username and :password options must be specified, ' \
339
+ 'but only :username given'
340
+ end
341
+
342
+ if !has_username && has_password
343
+ raise ::ArgumentError,
344
+ 'both :username and :password options must be specified, ' \
345
+ 'but only :password given'
346
+ end
347
+
348
+ username = options.delete(:username)
349
+ password = options.delete(:password)
350
+
351
+ Util.assert_instance_of(::String, username) do
352
+ ":username must be a String, #{username.inspect} given"
353
+ end
354
+ Util.assert_instance_of(::String, password) do
355
+ ":password must be a String, #{password.inspect} given"
356
+ end
357
+ Util.assert_not_empty(username) { ':username cannot be empty' }
358
+ Util.assert_not_empty(password) { ':password cannot be empty' }
359
+
360
+ options[:credentials] = {username: username, password: password}
361
+ options[:auth_provider] = Auth::Providers::Password.new(username, password)
362
+ end
363
+
364
+ if options.key?(:credentials)
365
+ credentials = options[:credentials]
366
+
367
+ Util.assert_instance_of(::Hash, credentials) do
368
+ ":credentials must be a hash, #{credentials.inspect} given"
369
+ end
370
+ end
371
+
372
+ if options.key?(:auth_provider)
373
+ auth_provider = options[:auth_provider]
374
+
375
+ Util.assert_responds_to(:create_authenticator, auth_provider) do
376
+ ":auth_provider #{auth_provider.inspect} must respond to " \
377
+ ":create_authenticator, but doesn't"
378
+ end
379
+ end
380
+
381
+ has_client_cert = options.key?(:client_cert)
382
+ has_private_key = options.key?(:private_key)
383
+
384
+ if has_client_cert || has_private_key
385
+ if has_client_cert && !has_private_key
386
+ raise ::ArgumentError,
387
+ 'both :client_cert and :private_key options must be specified, ' \
388
+ 'but only :client_cert given'
389
+ end
390
+
391
+ if !has_client_cert && has_private_key
392
+ raise ::ArgumentError,
393
+ 'both :client_cert and :private_key options must be specified, ' \
394
+ 'but only :private_key given'
395
+ end
396
+
397
+ Util.assert_instance_of(::String, options[:client_cert]) do
398
+ ":client_cert must be a string, #{options[:client_cert].inspect} given"
399
+ end
400
+ Util.assert_instance_of(::String, options[:private_key]) do
401
+ ":client_cert must be a string, #{options[:private_key].inspect} given"
402
+ end
403
+ client_cert = ::File.expand_path(options[:client_cert])
404
+ private_key = ::File.expand_path(options[:private_key])
405
+
406
+ Util.assert_file_exists(client_cert) do
407
+ ":client_cert #{client_cert.inspect} doesn't exist"
408
+ end
409
+ Util.assert_file_exists(private_key) do
410
+ ":private_key #{private_key.inspect} doesn't exist"
411
+ end
412
+ end
413
+
414
+ has_server_cert = options.key?(:server_cert)
415
+
416
+ if has_server_cert
417
+ Util.assert_instance_of(::String, options[:server_cert]) do
418
+ ":server_cert must be a string, #{options[:server_cert].inspect} given"
419
+ end
420
+ server_cert = ::File.expand_path(options[:server_cert])
421
+
422
+ Util.assert_file_exists(server_cert) do
423
+ ":server_cert #{server_cert.inspect} doesn't exist"
424
+ end
425
+ end
426
+
427
+ if has_client_cert || has_server_cert
428
+ context = ::OpenSSL::SSL::SSLContext.new
429
+
430
+ if has_server_cert
431
+ context.ca_file = server_cert
432
+ context.verify_mode = ::OpenSSL::SSL::VERIFY_PEER
433
+ end
434
+
435
+ if has_client_cert
436
+ context.cert = ::OpenSSL::X509::Certificate.new(File.read(client_cert))
437
+
438
+ context.key = if options.key?(:passphrase)
439
+ ::OpenSSL::PKey::RSA.new(File.read(private_key),
440
+ options[:passphrase])
441
+ else
442
+ ::OpenSSL::PKey::RSA.new(File.read(private_key))
443
+ end
444
+ end
445
+
446
+ options[:ssl] = context
447
+ end
448
+
449
+ if options.key?(:ssl)
450
+ ssl = options[:ssl]
451
+
452
+ Util.assert_instance_of_one_of(SSL_CLASSES, ssl) do
453
+ ":ssl must be a boolean or an OpenSSL::SSL::SSLContext, #{ssl.inspect} given"
454
+ end
455
+ end
456
+
457
+ if options.key?(:compression)
458
+ compression = options.delete(:compression)
459
+
460
+ case compression
461
+ when :snappy
462
+ options[:compressor] = Compression::Compressors::Snappy.new
463
+ when :lz4
464
+ options[:compressor] = Compression::Compressors::Lz4.new
465
+ else
466
+ raise ::ArgumentError,
467
+ ":compression must be either :snappy or :lz4, #{compression.inspect} given"
468
+ end
469
+ end
470
+
471
+ if options.key?(:compressor)
472
+ compressor = options[:compressor]
473
+ methods = [:algorithm, :compress?, :compress, :decompress]
474
+
475
+ Util.assert_responds_to_all(methods, compressor) do
476
+ ":compressor #{compressor.inspect} must respond to #{methods.inspect}, " \
477
+ "but doesn't"
478
+ end
479
+ end
480
+
481
+ if options.key?(:logger)
482
+ if options[:logger].nil?
483
+ # Delete the key because we want to fallback to the default logger in Driver.
484
+ options.delete(:logger)
485
+ else
486
+ # Validate
487
+ logger = options[:logger]
488
+ methods = [:debug, :info, :warn, :error, :fatal]
489
+
490
+ Util.assert_responds_to_all(methods, logger) do
491
+ ":logger #{logger.inspect} must respond to #{methods.inspect}, but doesn't"
492
+ end
493
+ end
494
+ end
495
+
496
+ if options.key?(:port)
497
+ unless options[:port].nil?
498
+ port = options[:port]
499
+ Util.assert_instance_of(::Integer, port)
500
+ Util.assert_one_of(1...2**16, port) do
501
+ ":port must be a valid ip port, #{port} given"
502
+ end
503
+ end
504
+ end
505
+
506
+ options[:datacenter] = String(options[:datacenter]) if options.key?(:datacenter)
507
+
508
+ if options.key?(:connect_timeout)
509
+ timeout = options[:connect_timeout]
510
+
511
+ unless timeout.nil?
512
+ Util.assert_instance_of(::Numeric, timeout) do
513
+ ":connect_timeout must be a number of seconds, #{timeout.inspect} given"
514
+ end
515
+ Util.assert(timeout > 0) do
516
+ ":connect_timeout must be greater than 0, #{timeout} given"
517
+ end
518
+ end
519
+ end
520
+
521
+ if options.key?(:execution_profiles)
522
+ Util.assert_instance_of(::Hash, options[:execution_profiles],
523
+ ':execution_profiles must be a hash of <name,ExecutionProfile> entries.')
524
+ end
525
+
526
+ if options.key?(:heartbeat_interval)
527
+ timeout = options[:heartbeat_interval]
528
+
529
+ unless timeout.nil?
530
+ Util.assert_instance_of(::Numeric, timeout) do
531
+ ":heartbeat_interval must be a number of seconds, #{timeout.inspect} given"
532
+ end
533
+ Util.assert(timeout > 0) do
534
+ ":heartbeat_interval must be greater than 0, #{timeout} given"
535
+ end
536
+ end
537
+ end
538
+
539
+ if options.key?(:idle_timeout)
540
+ timeout = options[:idle_timeout]
541
+
542
+ unless timeout.nil?
543
+ Util.assert_instance_of(::Numeric, timeout) do
544
+ ":idle_timeout must be a number of seconds, #{timeout.inspect} given"
545
+ end
546
+ Util.assert(timeout > 0) do
547
+ ":idle_timeout must be greater than 0, #{timeout} given"
548
+ end
549
+ end
550
+ end
551
+
552
+ if options.key?(:schema_refresh_delay)
553
+ timeout = options[:schema_refresh_delay]
554
+
555
+ Util.assert_instance_of(::Numeric, timeout) do
556
+ ":schema_refresh_delay must be a number of seconds, #{timeout.inspect} given"
557
+ end
558
+ Util.assert(timeout > 0) do
559
+ ":schema_refresh_delay must be greater than 0, #{timeout} given"
560
+ end
561
+ end
562
+
563
+ if options.key?(:schema_refresh_timeout)
564
+ timeout = options[:schema_refresh_timeout]
565
+
566
+ Util.assert_instance_of(::Numeric, timeout) do
567
+ ":schema_refresh_timeout must be a number of seconds, #{timeout.inspect} given"
568
+ end
569
+ Util.assert(timeout > 0) do
570
+ ":schema_refresh_timeout must be greater than 0, #{timeout} given"
571
+ end
572
+ end
573
+
574
+ if options.key?(:reconnection_policy)
575
+ reconnection_policy = options[:reconnection_policy]
576
+
577
+ Util.assert_responds_to(:schedule, reconnection_policy) do
578
+ ":reconnection_policy #{reconnection_policy.inspect} must respond to " \
579
+ ":schedule, but doesn't"
580
+ end
581
+ end
582
+
583
+ # Validate options that go in an execution profile. Instantiating one
584
+ # causes validation automatically.
585
+ Cassandra::Execution::Profile.new(options)
586
+
587
+ options[:listeners] = Array(options[:listeners]) if options.key?(:listeners)
588
+ options[:nodelay] = !!options[:nodelay] if options.key?(:nodelay)
589
+ options[:trace] = !!options[:trace] if options.key?(:trace)
590
+ options[:shuffle_replicas] = !!options[:shuffle_replicas] if options.key?(:shuffle_replicas)
591
+ options[:allow_beta_protocol] = !!options[:allow_beta_protocol] if options.key?(:allow_beta_protocol)
592
+
593
+ if options.key?(:page_size)
594
+ page_size = options[:page_size]
595
+
596
+ unless page_size.nil?
597
+ page_size = options[:page_size]
598
+ Util.assert_instance_of(::Integer, page_size)
599
+ Util.assert_one_of(1...2**32, page_size) do
600
+ ":page_size must be a positive integer, #{page_size.inspect} given"
601
+ end
602
+ end
603
+ end
604
+
605
+ if options.key?(:protocol_version)
606
+ protocol_version = options[:protocol_version]
607
+ unless protocol_version.nil?
608
+ Util.assert_instance_of(::Integer, protocol_version)
609
+ Util.assert_one_of(1..Cassandra::Protocol::Versions::MAX_SUPPORTED_VERSION, protocol_version,
610
+ ':protocol_version must be a positive integer between 1 and ' \
611
+ "#{Cassandra::Protocol::Versions::MAX_SUPPORTED_VERSION}, #{protocol_version.inspect} given")
612
+ end
613
+ end
614
+
615
+ Util.assert(!(options[:allow_beta_protocol] && options[:protocol_version]),
616
+ 'only one of :allow_beta_protocol and :protocol_version may be specified, both given')
617
+
618
+ if options.key?(:futures_factory)
619
+ futures_factory = options[:futures_factory]
620
+ methods = [:error, :value, :promise, :all]
621
+
622
+ Util.assert_responds_to_all(methods, futures_factory) do
623
+ ":futures_factory #{futures_factory.inspect} must respond to " \
624
+ "#{methods.inspect}, but doesn't"
625
+ end
626
+ end
627
+
628
+ if options.key?(:address_resolution)
629
+ address_resolution = options.delete(:address_resolution)
630
+
631
+ case address_resolution
632
+ when :none
633
+ # do nothing
634
+ when :ec2_multi_region
635
+ options[:address_resolution_policy] =
636
+ AddressResolution::Policies::EC2MultiRegion.new
637
+ else
638
+ raise ::ArgumentError,
639
+ ':address_resolution must be either :none or :ec2_multi_region, ' \
640
+ "#{address_resolution.inspect} given"
641
+ end
642
+ end
643
+
644
+ if options.key?(:address_resolution_policy)
645
+ address_resolver = options[:address_resolution_policy]
646
+
647
+ Util.assert_responds_to(:resolve, address_resolver) do
648
+ ':address_resolution_policy must respond to :resolve, ' \
649
+ "#{address_resolver.inspect} but doesn't"
650
+ end
651
+ end
652
+
653
+ options[:synchronize_schema] = !!options[:synchronize_schema] if options.key?(:synchronize_schema)
654
+
655
+ if options.key?(:client_timestamps)
656
+ timestamp_generator = case options[:client_timestamps]
657
+ when true
658
+ if RUBY_ENGINE == 'jruby'
659
+ Cassandra::TimestampGenerator::TickingOnDuplicate.new
660
+ else
661
+ Cassandra::TimestampGenerator::Simple.new
662
+ end
663
+ when false
664
+ nil
665
+ when :simple
666
+ Cassandra::TimestampGenerator::Simple.new
667
+ when :monotonic
668
+ Cassandra::TimestampGenerator::TickingOnDuplicate.new
669
+ else
670
+ # The value must be a generator instance.
671
+ options[:client_timestamps]
672
+ end
673
+
674
+ if timestamp_generator
675
+ Util.assert_responds_to(:next, timestamp_generator) do
676
+ ":client_timestamps #{options[:client_timestamps].inspect} must be a boolean, :simple, :monotonic, or " \
677
+ 'an object that responds to :next'
678
+ end
679
+ end
680
+ options.delete(:client_timestamps)
681
+ options[:timestamp_generator] = timestamp_generator
682
+ end
683
+
684
+ if options.key?(:connections_per_local_node)
685
+ connections_per_node = options[:connections_per_local_node]
686
+
687
+ unless connections_per_node.nil?
688
+ connections_per_node = options[:connections_per_local_node]
689
+ Util.assert_instance_of(::Integer, connections_per_node)
690
+ Util.assert_one_of(1...2**16, connections_per_node) do
691
+ ':connections_per_local_node must be a positive integer between ' \
692
+ "1 and 65535, #{connections_per_node.inspect} given"
693
+ end
694
+ end
695
+ end
696
+
697
+ if options.key?(:connections_per_remote_node)
698
+ connections_per_node = options[:connections_per_remote_node]
699
+
700
+ unless connections_per_node.nil?
701
+ connections_per_node = options[:connections_per_remote_node]
702
+ Util.assert_instance_of(::Integer, connections_per_node)
703
+ Util.assert_one_of(1...2**16, connections_per_node) do
704
+ ':connections_per_remote_node must be a positive integer between ' \
705
+ "1 and 65535, #{connections_per_node.inspect} given"
706
+ end
707
+ end
708
+ end
709
+
710
+ if options.key?(:requests_per_connection)
711
+ requests_per_connection = options[:requests_per_connection]
712
+
713
+ unless requests_per_connection.nil?
714
+ requests_per_connection = options[:requests_per_connection]
715
+ Util.assert_instance_of(::Integer, requests_per_connection)
716
+
717
+ # v3 protocol says that max stream-id is 32767 (2^15-1). This setting might be
718
+ # used to talk to a v2 (or less) node, but then we'll adjust it down.
719
+
720
+ Util.assert_one_of(1...2**15, requests_per_connection) do
721
+ ':requests_per_connection must be a positive integer, ' \
722
+ "#{requests_per_connection.inspect} given"
723
+ end
724
+ end
725
+ end
726
+
727
+ # Get host addresses.
728
+ hosts = []
729
+
730
+ Array(options.fetch(:hosts, '127.0.0.1')).each do |host|
731
+ case host
732
+ when ::IPAddr
733
+ hosts << host
734
+ when ::String # ip address or hostname
735
+ Resolv.each_address(host) do |ip|
736
+ hosts << ::IPAddr.new(ip)
737
+ end
738
+ else
739
+ raise ::ArgumentError, ":hosts must be String or IPAddr, #{host.inspect} given"
740
+ end
741
+ end
742
+
743
+ if hosts.empty?
744
+ raise ::ArgumentError,
745
+ ":hosts #{options[:hosts].inspect} could not be resolved to any ip address"
746
+ end
747
+
748
+ hosts.shuffle!
749
+
750
+ [options, hosts]
751
+ end
752
+
753
+ # @private
754
+ EMPTY_LIST = [].freeze
755
+ # @private
756
+ NOT_SET = ::Object.new
757
+ # @private
758
+ NULL_BYTE = "\x00".freeze
759
+
760
+ # @private
761
+ # ensures that:
762
+ # ::Date.jd(DATE_OFFSET, ::Date::GREGORIAN)
763
+ # => -5877641-06-23
764
+ # ::Date.jd(DATE_OFFSET + 2 ** 31, ::Date::GREGORIAN)
765
+ # => 1970-1-1
766
+ # ::Date.jd(DATE_OFFSET + 2 ** 32, ::Date::GREGORIAN)
767
+ # => 5881580-07-12
768
+ DATE_OFFSET = (::Time.utc(1970, 1, 1).to_date.jd - 2**31)
769
+ end
770
+
771
+ require 'cassandra/attr_boolean'
772
+ require 'cassandra/version'
773
+ require 'cassandra/uuid'
774
+ require 'cassandra/time_uuid'
775
+ require 'cassandra/tuple'
776
+ require 'cassandra/udt'
777
+ require 'cassandra/time'
778
+
779
+ require 'cassandra/types'
780
+
781
+ require 'cassandra/errors'
782
+ require 'cassandra/compression'
783
+ require 'cassandra/protocol'
784
+ require 'cassandra/auth'
785
+ require 'cassandra/cassandra_logger'
786
+ require 'cassandra/null_logger'
787
+
788
+ require 'cassandra/executors'
789
+ require 'cassandra/future'
790
+ require 'cassandra/cluster'
791
+ require 'cassandra/custom_data'
792
+ require 'cassandra/driver'
793
+ require 'cassandra/host'
794
+ require 'cassandra/session'
795
+ require 'cassandra/result'
796
+ require 'cassandra/statement'
797
+ require 'cassandra/statements'
798
+
799
+ require 'cassandra/aggregate'
800
+ require 'cassandra/argument'
801
+ require 'cassandra/function'
802
+ require 'cassandra/function_collection'
803
+ require 'cassandra/column'
804
+ require 'cassandra/column_container'
805
+ require 'cassandra/table'
806
+ require 'cassandra/materialized_view'
807
+ require 'cassandra/keyspace'
808
+ require 'cassandra/index'
809
+ require 'cassandra/trigger'
810
+
811
+ require 'cassandra/execution/info'
812
+ require 'cassandra/execution/options'
813
+ require 'cassandra/execution/profile_manager'
814
+ require 'cassandra/execution/profile'
815
+ require 'cassandra/execution/trace'
816
+
817
+ require 'cassandra/load_balancing'
818
+ require 'cassandra/reconnection'
819
+ require 'cassandra/retry'
820
+ require 'cassandra/address_resolution'
821
+ require 'cassandra/timestamp_generator'
822
+
823
+ require 'cassandra/util'
824
+
825
+ # murmur3 hash extension
826
+ require 'cassandra_murmur3'
827
+
828
+ # SortedSet has a race condition where it does some class/global initialization when the first instance is created.
829
+ # If this is done in a multi-threaded environment, bad things can happen. So force the initialization here,
830
+ # when loading the C* module.
831
+ ::SortedSet.new
832
+
833
+ module Cassandra
834
+ # @private
835
+ VOID_STATEMENT = Statements::Void.new
836
+ # @private
837
+ VOID_OPTIONS = Execution::Options.new(consistency: :quorum,
838
+ load_balancing_policy: LoadBalancing::Policies::RoundRobin.new,
839
+ retry_policy: Retry::Policies::Default.new)
840
+ # @private
841
+ NO_HOSTS = Errors::NoHostsAvailable.new
842
+ end