yugabyte-ycql-driver 3.2.3.1

Sign up to get free protection for your applications and to get access to all the features.
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