cassandra-driver 1.0.0.beta.2-java
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.
- checksums.yaml +7 -0
- data/.yardopts +4 -0
- data/README.md +125 -0
- data/lib/cassandra/auth/providers/password.rb +73 -0
- data/lib/cassandra/auth/providers.rb +16 -0
- data/lib/cassandra/auth.rb +97 -0
- data/lib/cassandra/client/batch.rb +212 -0
- data/lib/cassandra/client/client.rb +591 -0
- data/lib/cassandra/client/column_metadata.rb +54 -0
- data/lib/cassandra/client/connection_manager.rb +72 -0
- data/lib/cassandra/client/connector.rb +277 -0
- data/lib/cassandra/client/execute_options_decoder.rb +59 -0
- data/lib/cassandra/client/null_logger.rb +37 -0
- data/lib/cassandra/client/peer_discovery.rb +50 -0
- data/lib/cassandra/client/prepared_statement.rb +314 -0
- data/lib/cassandra/client/query_result.rb +230 -0
- data/lib/cassandra/client/request_runner.rb +71 -0
- data/lib/cassandra/client/result_metadata.rb +48 -0
- data/lib/cassandra/client/void_result.rb +78 -0
- data/lib/cassandra/client.rb +144 -0
- data/lib/cassandra/cluster/client.rb +768 -0
- data/lib/cassandra/cluster/connector.rb +244 -0
- data/lib/cassandra/cluster/control_connection.rb +425 -0
- data/lib/cassandra/cluster/metadata.rb +124 -0
- data/lib/cassandra/cluster/options.rb +42 -0
- data/lib/cassandra/cluster/registry.rb +198 -0
- data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +47 -0
- data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
- data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +92 -0
- data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
- data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
- data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
- data/lib/cassandra/cluster/schema/type_parser.rb +138 -0
- data/lib/cassandra/cluster/schema.rb +340 -0
- data/lib/cassandra/cluster.rb +215 -0
- data/lib/cassandra/column.rb +92 -0
- data/lib/cassandra/compression/compressors/lz4.rb +72 -0
- data/lib/cassandra/compression/compressors/snappy.rb +66 -0
- data/lib/cassandra/compression.rb +66 -0
- data/lib/cassandra/driver.rb +111 -0
- data/lib/cassandra/errors.rb +79 -0
- data/lib/cassandra/execution/info.rb +51 -0
- data/lib/cassandra/execution/options.rb +80 -0
- data/lib/cassandra/execution/trace.rb +152 -0
- data/lib/cassandra/future.rb +675 -0
- data/lib/cassandra/host.rb +79 -0
- data/lib/cassandra/keyspace.rb +133 -0
- data/lib/cassandra/listener.rb +87 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +149 -0
- data/lib/cassandra/load_balancing/policies/round_robin.rb +132 -0
- data/lib/cassandra/load_balancing/policies/token_aware.rb +119 -0
- data/lib/cassandra/load_balancing/policies/white_list.rb +90 -0
- data/lib/cassandra/load_balancing/policies.rb +19 -0
- data/lib/cassandra/load_balancing.rb +113 -0
- data/lib/cassandra/protocol/cql_byte_buffer.rb +307 -0
- data/lib/cassandra/protocol/cql_protocol_handler.rb +323 -0
- data/lib/cassandra/protocol/frame_decoder.rb +128 -0
- data/lib/cassandra/protocol/frame_encoder.rb +48 -0
- data/lib/cassandra/protocol/request.rb +38 -0
- data/lib/cassandra/protocol/requests/auth_response_request.rb +47 -0
- data/lib/cassandra/protocol/requests/batch_request.rb +76 -0
- data/lib/cassandra/protocol/requests/credentials_request.rb +47 -0
- data/lib/cassandra/protocol/requests/execute_request.rb +103 -0
- data/lib/cassandra/protocol/requests/options_request.rb +39 -0
- data/lib/cassandra/protocol/requests/prepare_request.rb +50 -0
- data/lib/cassandra/protocol/requests/query_request.rb +153 -0
- data/lib/cassandra/protocol/requests/register_request.rb +38 -0
- data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
- data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
- data/lib/cassandra/protocol/response.rb +38 -0
- data/lib/cassandra/protocol/responses/auth_challenge_response.rb +41 -0
- data/lib/cassandra/protocol/responses/auth_success_response.rb +41 -0
- data/lib/cassandra/protocol/responses/authenticate_response.rb +41 -0
- data/lib/cassandra/protocol/responses/detailed_error_response.rb +60 -0
- data/lib/cassandra/protocol/responses/error_response.rb +50 -0
- data/lib/cassandra/protocol/responses/event_response.rb +39 -0
- data/lib/cassandra/protocol/responses/prepared_result_response.rb +64 -0
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +43 -0
- data/lib/cassandra/protocol/responses/ready_response.rb +44 -0
- data/lib/cassandra/protocol/responses/result_response.rb +48 -0
- data/lib/cassandra/protocol/responses/rows_result_response.rb +139 -0
- data/lib/cassandra/protocol/responses/schema_change_event_response.rb +60 -0
- data/lib/cassandra/protocol/responses/schema_change_result_response.rb +57 -0
- data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +42 -0
- data/lib/cassandra/protocol/responses/status_change_event_response.rb +44 -0
- data/lib/cassandra/protocol/responses/supported_response.rb +41 -0
- data/lib/cassandra/protocol/responses/topology_change_event_response.rb +34 -0
- data/lib/cassandra/protocol/responses/void_result_response.rb +39 -0
- data/lib/cassandra/protocol/type_converter.rb +384 -0
- data/lib/cassandra/protocol.rb +93 -0
- data/lib/cassandra/reconnection/policies/constant.rb +48 -0
- data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
- data/lib/cassandra/reconnection/policies.rb +20 -0
- data/lib/cassandra/reconnection.rb +49 -0
- data/lib/cassandra/result.rb +215 -0
- data/lib/cassandra/retry/policies/default.rb +47 -0
- data/lib/cassandra/retry/policies/downgrading_consistency.rb +71 -0
- data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
- data/lib/cassandra/retry/policies.rb +21 -0
- data/lib/cassandra/retry.rb +142 -0
- data/lib/cassandra/session.rb +202 -0
- data/lib/cassandra/statement.rb +22 -0
- data/lib/cassandra/statements/batch.rb +95 -0
- data/lib/cassandra/statements/bound.rb +48 -0
- data/lib/cassandra/statements/prepared.rb +81 -0
- data/lib/cassandra/statements/simple.rb +58 -0
- data/lib/cassandra/statements/void.rb +33 -0
- data/lib/cassandra/statements.rb +23 -0
- data/lib/cassandra/table.rb +299 -0
- data/lib/cassandra/time_uuid.rb +142 -0
- data/lib/cassandra/util.rb +167 -0
- data/lib/cassandra/uuid.rb +104 -0
- data/lib/cassandra/version.rb +21 -0
- data/lib/cassandra.rb +428 -0
- data/lib/cassandra_murmur3.jar +0 -0
- metadata +211 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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 cassandra table
|
|
21
|
+
# @see Cassandra::Keyspace#each_table
|
|
22
|
+
# @see Cassandra::Keyspace#table
|
|
23
|
+
class Table
|
|
24
|
+
# @private
|
|
25
|
+
class Options
|
|
26
|
+
attr_reader :comment, :read_repair_chance, :local_read_repair_chance,
|
|
27
|
+
:gc_grace_seconds, :caching, :bloom_filter_fp_chance,
|
|
28
|
+
:populate_io_cache_on_flush, :memtable_flush_period_in_ms,
|
|
29
|
+
:default_time_to_live, :speculative_retry, :index_interval,
|
|
30
|
+
:replicate_on_write, :compaction_strategy, :compact_storage,
|
|
31
|
+
:compression_parameters
|
|
32
|
+
|
|
33
|
+
def initialize(data, compaction_strategy, compression_parameters, compact_storage, cassandra_version)
|
|
34
|
+
@comment = data['comment']
|
|
35
|
+
@read_repair_chance = data['read_repair_chance']
|
|
36
|
+
@local_read_repair_chance = data['local_read_repair_chance']
|
|
37
|
+
@gc_grace_seconds = data['gc_grace_seconds']
|
|
38
|
+
@caching = data['caching']
|
|
39
|
+
@bloom_filter_fp_chance = data['bloom_filter_fp_chance'] || 0.01
|
|
40
|
+
@populate_io_cache_on_flush = data['populate_io_cache_on_flush'] || false
|
|
41
|
+
@memtable_flush_period_in_ms = data['memtable_flush_period_in_ms'] || 0
|
|
42
|
+
@default_time_to_live = data['default_time_to_live'] || 0
|
|
43
|
+
@speculative_retry = data['speculative_retry'] || 'NONE'
|
|
44
|
+
@index_interval = data['index_interval'] || 128
|
|
45
|
+
@replicate_on_write = data['replicate_on_write'] || true
|
|
46
|
+
@compaction_strategy = compaction_strategy
|
|
47
|
+
@compression_parameters = compression_parameters
|
|
48
|
+
@compact_storage = compact_storage
|
|
49
|
+
@cassandra_version = cassandra_version
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def replicate_on_write?
|
|
53
|
+
@replicate_on_write
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def populate_io_cache_on_flush?
|
|
57
|
+
@populate_io_cache_on_flush
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def compact_storage?
|
|
61
|
+
@compact_storage
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def to_cql
|
|
65
|
+
options = []
|
|
66
|
+
|
|
67
|
+
options << 'COMPACT STORAGE' if @compact_storage
|
|
68
|
+
options << "bloom_filter_fp_chance = #{@bloom_filter_fp_chance}"
|
|
69
|
+
options << "caching = '#{@caching}'"
|
|
70
|
+
options << "comment = '#{@comment}'" if @comment
|
|
71
|
+
options << "compaction = #{@compaction_strategy.to_cql}"
|
|
72
|
+
options << "compression = #{Util.encode_hash(@compression_parameters)}"
|
|
73
|
+
options << "dclocal_read_repair_chance = #{@local_read_repair_chance}"
|
|
74
|
+
options << "default_time_to_live = #{@default_time_to_live}" if !@cassandra_version.start_with?('1')
|
|
75
|
+
options << "gc_grace_seconds = #{@gc_grace_seconds}"
|
|
76
|
+
options << "index_interval = #{@index_interval}" if !@cassandra_version.start_with?('1')
|
|
77
|
+
options << "populate_io_cache_on_flush = '#{@populate_io_cache_on_flush}'"
|
|
78
|
+
options << "read_repair_chance = #{@read_repair_chance}"
|
|
79
|
+
options << "replicate_on_write = '#{@replicate_on_write}'" if @cassandra_version.start_with?('1') || @cassandra_version.start_with?('2.0')
|
|
80
|
+
options << "speculative_retry = '#{@speculative_retry}'" if !@cassandra_version.start_with?('1')
|
|
81
|
+
|
|
82
|
+
options.join("\nAND ")
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def eql?(other)
|
|
86
|
+
other.is_a?(Options) &&
|
|
87
|
+
@comment == other.comment &&
|
|
88
|
+
@read_repair_chance == other.read_repair_chance &&
|
|
89
|
+
@local_read_repair_chance == other.local_read_repair_chance &&
|
|
90
|
+
@gc_grace_seconds == other.gc_grace_seconds &&
|
|
91
|
+
@caching == other.caching &&
|
|
92
|
+
@bloom_filter_fp_chance == other.bloom_filter_fp_chance &&
|
|
93
|
+
@populate_io_cache_on_flush == other.populate_io_cache_on_flush &&
|
|
94
|
+
@memtable_flush_period_in_ms == other.memtable_flush_period_in_ms &&
|
|
95
|
+
@default_time_to_live == other.default_time_to_live &&
|
|
96
|
+
@speculative_retry == other.speculative_retry &&
|
|
97
|
+
@index_interval == other.index_interval &&
|
|
98
|
+
@replicate_on_write == other.replicate_on_write &&
|
|
99
|
+
@compaction_strategy == other.compaction_strategy &&
|
|
100
|
+
@compression_parameters == other.compression_parameters &&
|
|
101
|
+
@compact_storage == other.compact_storage &&
|
|
102
|
+
@cassandra_version == other.cassandra_version
|
|
103
|
+
end
|
|
104
|
+
alias :== :eql?
|
|
105
|
+
|
|
106
|
+
attr_reader :cassandra_version
|
|
107
|
+
protected :cassandra_version
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# @private
|
|
111
|
+
class Compaction
|
|
112
|
+
attr_reader :klass, :options
|
|
113
|
+
|
|
114
|
+
def initialize(klass, options)
|
|
115
|
+
@klass = klass
|
|
116
|
+
@options = options
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def to_cql
|
|
120
|
+
compaction = {'class' => @klass}
|
|
121
|
+
compaction.merge!(@options)
|
|
122
|
+
|
|
123
|
+
Util.encode_hash(compaction)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def eql?(other)
|
|
127
|
+
other.is_a?(Compaction) &&
|
|
128
|
+
@klass == other.klass &&
|
|
129
|
+
@options == other.options
|
|
130
|
+
end
|
|
131
|
+
alias :== :eql?
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @private
|
|
135
|
+
attr_reader :keyspace
|
|
136
|
+
# @return [String] table name
|
|
137
|
+
attr_reader :name
|
|
138
|
+
# @private
|
|
139
|
+
attr_reader :options
|
|
140
|
+
|
|
141
|
+
# @private
|
|
142
|
+
def initialize(keyspace, name, partition_key, clustering_columns, columns, options, clustering_order)
|
|
143
|
+
@keyspace = keyspace
|
|
144
|
+
@name = name
|
|
145
|
+
@partition_key = partition_key
|
|
146
|
+
@clustering_columns = clustering_columns
|
|
147
|
+
@columns = columns
|
|
148
|
+
@options = options
|
|
149
|
+
@clustering_order = clustering_order
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# @param name [String] column name
|
|
153
|
+
# @return [Boolean] whether this table has a given column
|
|
154
|
+
def has_column?(name)
|
|
155
|
+
@columns.has_key?(name)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# @param name [String] column name
|
|
159
|
+
# @return [Cassandra::Column, nil] a column or nil
|
|
160
|
+
def column(name)
|
|
161
|
+
@columns[name]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Yield or enumerate each column defined in this table
|
|
165
|
+
# @overload each_column
|
|
166
|
+
# @yieldparam column [Cassandra::Column] current column
|
|
167
|
+
# @return [Cassandra::Table] self
|
|
168
|
+
# @overload each_column
|
|
169
|
+
# @return [Array<Cassandra::Column>] a list of columns
|
|
170
|
+
def each_column(&block)
|
|
171
|
+
if block_given?
|
|
172
|
+
@columns.each_value(&block)
|
|
173
|
+
self
|
|
174
|
+
else
|
|
175
|
+
@columns.values
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
alias :columns :each_column
|
|
179
|
+
|
|
180
|
+
# @return [String] a cql representation of this table
|
|
181
|
+
def to_cql
|
|
182
|
+
cql = "CREATE TABLE #{Util.escape_name(@keyspace)}.#{Util.escape_name(@name)} (\n"
|
|
183
|
+
first = true
|
|
184
|
+
@columns.each do |(_, column)|
|
|
185
|
+
if first
|
|
186
|
+
first = false
|
|
187
|
+
else
|
|
188
|
+
cql << ",\n" unless first
|
|
189
|
+
end
|
|
190
|
+
cql << " #{column.to_cql}"
|
|
191
|
+
end
|
|
192
|
+
cql << ",\n PRIMARY KEY ("
|
|
193
|
+
if @partition_key.one?
|
|
194
|
+
cql << @partition_key.first.name
|
|
195
|
+
else
|
|
196
|
+
cql << '('
|
|
197
|
+
first = true
|
|
198
|
+
@partition_key.each do |column|
|
|
199
|
+
if first
|
|
200
|
+
first = false
|
|
201
|
+
else
|
|
202
|
+
cql << ', '
|
|
203
|
+
end
|
|
204
|
+
cql << column.name
|
|
205
|
+
end
|
|
206
|
+
cql << ')'
|
|
207
|
+
end
|
|
208
|
+
@clustering_columns.each do |column|
|
|
209
|
+
cql << ", #{column.name}"
|
|
210
|
+
end
|
|
211
|
+
cql << ')'
|
|
212
|
+
|
|
213
|
+
cql << "\n)\nWITH "
|
|
214
|
+
|
|
215
|
+
if @clustering_order.any? {|o| o != :asc}
|
|
216
|
+
cql << "CLUSTERING ORDER BY ("
|
|
217
|
+
first = true
|
|
218
|
+
@clustering_columns.zip(@clustering_order) do |column, order|
|
|
219
|
+
if first
|
|
220
|
+
first = false
|
|
221
|
+
else
|
|
222
|
+
cql << ', '
|
|
223
|
+
end
|
|
224
|
+
cql << "#{column.name} #{order.to_s.upcase}"
|
|
225
|
+
end
|
|
226
|
+
cql << ")\n AND "
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
cql << @options.to_cql.split("\n").join("\n ")
|
|
230
|
+
|
|
231
|
+
cql << ';'
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# @return [Boolean] whether this table is equal to the other
|
|
235
|
+
def eql?(other)
|
|
236
|
+
other.is_a?(Table) &&
|
|
237
|
+
@keyspace == other.keyspace &&
|
|
238
|
+
@name == other.name &&
|
|
239
|
+
@partition_key == other.partition_key &&
|
|
240
|
+
@clustering_columns == other.clustering_columns &&
|
|
241
|
+
@columns == other.raw_columns &&
|
|
242
|
+
@options == other.options &&
|
|
243
|
+
@clustering_order == other.clustering_order
|
|
244
|
+
end
|
|
245
|
+
alias :== :eql?
|
|
246
|
+
|
|
247
|
+
# @private
|
|
248
|
+
def create_partition_key(values)
|
|
249
|
+
partition_key = @partition_key
|
|
250
|
+
return nil unless partition_key.size == values.size
|
|
251
|
+
|
|
252
|
+
if partition_key.one?
|
|
253
|
+
column = partition_key.first
|
|
254
|
+
column_name = column.name
|
|
255
|
+
return nil unless values.has_key?(column_name)
|
|
256
|
+
|
|
257
|
+
buffer = Protocol::CqlByteBuffer.new
|
|
258
|
+
|
|
259
|
+
TYPE_CONVERTER.to_bytes(buffer, column.type, values[column_name])
|
|
260
|
+
buffer.discard(4)
|
|
261
|
+
else
|
|
262
|
+
buf = nil
|
|
263
|
+
buffer = nil
|
|
264
|
+
|
|
265
|
+
partition_key.each do |column|
|
|
266
|
+
column_name = column.name
|
|
267
|
+
return nil unless values.has_key?(column_name)
|
|
268
|
+
|
|
269
|
+
buf ||= Protocol::CqlByteBuffer.new
|
|
270
|
+
buffer ||= Protocol::CqlByteBuffer.new
|
|
271
|
+
|
|
272
|
+
TYPE_CONVERTER.to_bytes(buf, column.type, values[column_name])
|
|
273
|
+
buf.discard(4) # discard size
|
|
274
|
+
|
|
275
|
+
size = buf.length
|
|
276
|
+
buffer.append_short(size)
|
|
277
|
+
buffer << buf.read(size) << NULL_BYTE
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
buffer.to_str
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
private
|
|
285
|
+
|
|
286
|
+
NULL_BYTE = "\x00".freeze
|
|
287
|
+
TYPE_CONVERTER = Protocol::TypeConverter.new
|
|
288
|
+
|
|
289
|
+
attr_reader :partition_key, :clustering_columns, :clustering_order
|
|
290
|
+
protected :partition_key, :clustering_columns, :clustering_order
|
|
291
|
+
|
|
292
|
+
protected
|
|
293
|
+
|
|
294
|
+
# @private
|
|
295
|
+
def raw_columns
|
|
296
|
+
@columns
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
# A variant of UUID which can extract its time component.
|
|
21
|
+
#
|
|
22
|
+
class TimeUuid < Uuid
|
|
23
|
+
include Comparable
|
|
24
|
+
|
|
25
|
+
# Returns the time component from this UUID as a Time.
|
|
26
|
+
#
|
|
27
|
+
# @return [Time]
|
|
28
|
+
def to_time
|
|
29
|
+
t = time_bits - GREGORIAN_OFFSET
|
|
30
|
+
seconds = t/10_000_000
|
|
31
|
+
microseconds = (t - seconds * 10_000_000)/10.0
|
|
32
|
+
Time.at(seconds, microseconds).utc
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def <=>(other)
|
|
36
|
+
return nil unless other.kind_of?(Cassandra::Uuid)
|
|
37
|
+
c = self.value <=> other.value
|
|
38
|
+
return c if c == 0
|
|
39
|
+
self.time_bits <=> other.time_bits
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
def time_bits
|
|
45
|
+
n = (value >> 64)
|
|
46
|
+
t = 0
|
|
47
|
+
t |= (n & 0x0000000000000fff) << 48
|
|
48
|
+
t |= (n & 0x00000000ffff0000) << 16
|
|
49
|
+
t |= (n & 0xffffffff00000000) >> 32
|
|
50
|
+
t
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# @private
|
|
56
|
+
LOWER_HALF_MASK = 0xffffffff_ffffffff
|
|
57
|
+
|
|
58
|
+
public
|
|
59
|
+
|
|
60
|
+
# A UUID version 1, variant 1 generator. It can generate a sequence of UUIDs
|
|
61
|
+
# with reasonable uniqueness guarantees:
|
|
62
|
+
#
|
|
63
|
+
# * The clock ID and node ID components are set to random numbers when the
|
|
64
|
+
# generator is created.
|
|
65
|
+
# * If two calls to {#next} happen within the time afforded by the system
|
|
66
|
+
# clock resolution a counter is incremented and added to the time
|
|
67
|
+
# component.
|
|
68
|
+
# * If the clock moves backwards the clock ID is reset to a new random
|
|
69
|
+
# number.
|
|
70
|
+
#
|
|
71
|
+
# Instances of this class are absolutely not threadsafe. You should
|
|
72
|
+
# never share instances between threads.
|
|
73
|
+
#
|
|
74
|
+
class Generator
|
|
75
|
+
# Create a new UUID generator.
|
|
76
|
+
#
|
|
77
|
+
# @param [Integer] node_id an alternate node ID (defaults to a random number)
|
|
78
|
+
# @param [Integer] clock_id an alternate clock ID (defaults to a random number)
|
|
79
|
+
#
|
|
80
|
+
def initialize(node_id=nil, clock_id=nil, clock=Time)
|
|
81
|
+
@node_id = node_id || (rand(2**47) | 0x010000000000)
|
|
82
|
+
@clock_id = clock_id || rand(2**16)
|
|
83
|
+
@clock = clock
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Returns a new UUID with a time component that is the current time.
|
|
87
|
+
#
|
|
88
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
|
89
|
+
#
|
|
90
|
+
def next
|
|
91
|
+
now = @clock.now
|
|
92
|
+
usecs = now.to_i * 1_000_000 + now.usec
|
|
93
|
+
if @last_usecs && @last_usecs - @sequence <= usecs && usecs <= @last_usecs
|
|
94
|
+
@sequence += 1
|
|
95
|
+
elsif @last_usecs && @last_usecs > usecs
|
|
96
|
+
@sequence = 0
|
|
97
|
+
@clock_id = rand(2**16)
|
|
98
|
+
else
|
|
99
|
+
@sequence = 0
|
|
100
|
+
end
|
|
101
|
+
@last_usecs = usecs + @sequence
|
|
102
|
+
from_usecs(@last_usecs)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Returns a new UUID with a time component based on the specified Time.
|
|
106
|
+
# A piece of jitter is added to ensure that multiple calls with the same
|
|
107
|
+
# time do not generate the same UUID (if you want determinism you can set
|
|
108
|
+
# the second parameter to zero).
|
|
109
|
+
#
|
|
110
|
+
# @param [Time] time the time to encode into the UUID
|
|
111
|
+
# @param [Integer] jitter a number of microseconds to add to the time to make it unique
|
|
112
|
+
# @return [Cassandra::TimeUuid] a new UUID
|
|
113
|
+
#
|
|
114
|
+
def from_time(time, jitter=rand(2**16))
|
|
115
|
+
usecs = time.to_i * 1_000_000 + time.usec + jitter
|
|
116
|
+
from_usecs(usecs)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @private
|
|
120
|
+
def from_usecs(usecs)
|
|
121
|
+
t = GREGORIAN_OFFSET + usecs * 10
|
|
122
|
+
time_hi = t & 0x0fff000000000000
|
|
123
|
+
time_mid = t & 0x0000ffff00000000
|
|
124
|
+
time_low = t & 0x00000000ffffffff
|
|
125
|
+
version = 1
|
|
126
|
+
clock_id = @clock_id & 0x3fff
|
|
127
|
+
node_id = @node_id & 0xffffffffffff
|
|
128
|
+
variant = 0x8000
|
|
129
|
+
n = (time_low << 96) | (time_mid << 48) | (time_hi << 16)
|
|
130
|
+
n |= version << 76
|
|
131
|
+
n |= (clock_id | variant) << 48
|
|
132
|
+
n |= node_id
|
|
133
|
+
TimeUuid.new(n)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
# @private
|
|
140
|
+
GREGORIAN_OFFSET = 122192928000000000
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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
|
+
# @private
|
|
21
|
+
module Util extend self
|
|
22
|
+
def encode_hash(hash, io = StringIO.new)
|
|
23
|
+
first = true
|
|
24
|
+
|
|
25
|
+
io.putc(CRL_OPN)
|
|
26
|
+
hash.each do |k, v|
|
|
27
|
+
if first
|
|
28
|
+
first = false
|
|
29
|
+
else
|
|
30
|
+
io.print(COMMA)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
encode_object(k, io)
|
|
34
|
+
io.print(COLON)
|
|
35
|
+
encode_object(v, io)
|
|
36
|
+
end
|
|
37
|
+
io.putc(CRL_CLS)
|
|
38
|
+
|
|
39
|
+
io.string
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def encode_set(set, io = StringIO.new)
|
|
43
|
+
first = true
|
|
44
|
+
|
|
45
|
+
io.putc(CRL_OPN)
|
|
46
|
+
set.each do |object|
|
|
47
|
+
if first
|
|
48
|
+
first = false
|
|
49
|
+
else
|
|
50
|
+
io.print(COMMA)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
encode_object(object, io)
|
|
54
|
+
end
|
|
55
|
+
io.putc(CRL_CLS)
|
|
56
|
+
|
|
57
|
+
io.string
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def encode_array(array, io = StringIO.new)
|
|
61
|
+
first = true
|
|
62
|
+
|
|
63
|
+
io.putc(SQR_OPN)
|
|
64
|
+
array.each do |object|
|
|
65
|
+
if first
|
|
66
|
+
first = false
|
|
67
|
+
else
|
|
68
|
+
io.print(COMMA)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
encode_object(object, io)
|
|
72
|
+
end
|
|
73
|
+
io.putc(SQR_CLS)
|
|
74
|
+
|
|
75
|
+
io.string
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def encode_string(string, io = StringIO.new)
|
|
79
|
+
io.putc(QUOT)
|
|
80
|
+
string.chars do |c|
|
|
81
|
+
case c
|
|
82
|
+
when QUOT then io.print(ESC_QUOT)
|
|
83
|
+
else
|
|
84
|
+
io.putc(c)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
io.putc(QUOT)
|
|
88
|
+
|
|
89
|
+
io.string
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def encode_object(object, io = StringIO.new)
|
|
93
|
+
case object
|
|
94
|
+
when ::Hash then encode_hash(object, io)
|
|
95
|
+
when ::Array then encode_array(object, io)
|
|
96
|
+
when ::Set then encode_set(object, io)
|
|
97
|
+
when ::String then encode_string(object, io)
|
|
98
|
+
when ::Time then encode_timestamp(object, io)
|
|
99
|
+
when ::Numeric then encode_number(object, io)
|
|
100
|
+
when ::IPAddr then encode_inet(object, io)
|
|
101
|
+
when Uuid then encode_uuid(object, io)
|
|
102
|
+
when nil then io.print(NULL_STR)
|
|
103
|
+
when false then io.print(FALSE_STR)
|
|
104
|
+
when true then io.print(TRUE_STR)
|
|
105
|
+
else
|
|
106
|
+
raise "unsupported type: #{object.inspect}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
io.string
|
|
110
|
+
end
|
|
111
|
+
alias :encode :encode_object
|
|
112
|
+
|
|
113
|
+
def encode_timestamp(time, io = StringIO.new)
|
|
114
|
+
io.print(time.to_i)
|
|
115
|
+
io.string
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def encode_number(number, io = StringIO.new)
|
|
119
|
+
io.print(number)
|
|
120
|
+
io.string
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def encode_uuid(uuid, io = StringIO.new)
|
|
124
|
+
io.print(uuid)
|
|
125
|
+
io.string
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def encode_inet(inet, io = StringIO.new)
|
|
129
|
+
io.putc(QUOT)
|
|
130
|
+
io.print(inet)
|
|
131
|
+
io.putc(QUOT)
|
|
132
|
+
io.string
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def escape_name(name)
|
|
136
|
+
return name if name[LOWERCASE_REGEXP] == name
|
|
137
|
+
DBL_QUOT + name + DBL_QUOT
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @private
|
|
141
|
+
LOWERCASE_REGEXP = /[[:lower:]\_]*/
|
|
142
|
+
# @private
|
|
143
|
+
NULL_STR = 'null'.freeze
|
|
144
|
+
# @private
|
|
145
|
+
FALSE_STR = 'false'.freeze
|
|
146
|
+
# @private
|
|
147
|
+
TRUE_STR = 'true'.freeze
|
|
148
|
+
# @private
|
|
149
|
+
CRL_OPN = '{'.freeze
|
|
150
|
+
# @private
|
|
151
|
+
CRL_CLS = '}'.freeze
|
|
152
|
+
# @private
|
|
153
|
+
SQR_OPN = '['.freeze
|
|
154
|
+
# @private
|
|
155
|
+
SQR_CLS = ']'.freeze
|
|
156
|
+
# @private
|
|
157
|
+
COMMA = ', '.freeze
|
|
158
|
+
# @private
|
|
159
|
+
COLON = ' : '.freeze
|
|
160
|
+
# @private
|
|
161
|
+
QUOT = ?'.freeze
|
|
162
|
+
# @private
|
|
163
|
+
ESC_QUOT = "''".freeze
|
|
164
|
+
# @private
|
|
165
|
+
DBL_QUOT = ?".freeze
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 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::TimeUuid::Generator}.
|
|
26
|
+
#
|
|
27
|
+
class Uuid
|
|
28
|
+
# Creates a new UUID either from a string (expected to be on the standard 8-4-4-4-12 form, or just 32 characters without hyphens), or from a 128 bit number.
|
|
29
|
+
#
|
|
30
|
+
# @param uuid [String] a 32 char uuid
|
|
31
|
+
#
|
|
32
|
+
# @raise [ArgumentError] if the string does not conform to the expected format
|
|
33
|
+
#
|
|
34
|
+
def initialize(uuid)
|
|
35
|
+
case uuid
|
|
36
|
+
when String
|
|
37
|
+
@n = from_s(uuid)
|
|
38
|
+
else
|
|
39
|
+
@n = uuid
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Returns a string representation of this UUID in the standard 8-4-4-4-12 form.
|
|
44
|
+
#
|
|
45
|
+
def to_s
|
|
46
|
+
@s ||= begin
|
|
47
|
+
s = RAW_FORMAT % @n
|
|
48
|
+
s.insert(20, HYPHEN)
|
|
49
|
+
s.insert(16, HYPHEN)
|
|
50
|
+
s.insert(12, HYPHEN)
|
|
51
|
+
s.insert( 8, HYPHEN)
|
|
52
|
+
s
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def hash
|
|
57
|
+
@n.hash
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the numerical representation of this UUID
|
|
61
|
+
#
|
|
62
|
+
# @return [Bignum] the 128 bit numerical representation
|
|
63
|
+
#
|
|
64
|
+
def value
|
|
65
|
+
@n
|
|
66
|
+
end
|
|
67
|
+
alias_method :to_i, :value
|
|
68
|
+
|
|
69
|
+
# @private
|
|
70
|
+
def eql?(other)
|
|
71
|
+
other.respond_to?(:value) && self.value == other.value
|
|
72
|
+
end
|
|
73
|
+
alias_method :==, :eql?
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
# @private
|
|
78
|
+
RAW_FORMAT = '%032x'.force_encoding(Encoding::ASCII).freeze
|
|
79
|
+
# @private
|
|
80
|
+
HYPHEN = '-'.force_encoding(Encoding::ASCII).freeze
|
|
81
|
+
# @private
|
|
82
|
+
EMPTY_STRING = ''.freeze
|
|
83
|
+
|
|
84
|
+
if RUBY_ENGINE == 'jruby'
|
|
85
|
+
# @private
|
|
86
|
+
HEX_RE = /^[A-Fa-f0-9]+$/
|
|
87
|
+
# See https://github.com/jruby/jruby/issues/1608
|
|
88
|
+
# @private
|
|
89
|
+
def from_s(str)
|
|
90
|
+
str = str.gsub(HYPHEN, EMPTY_STRING)
|
|
91
|
+
raise ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
|
|
92
|
+
raise ArgumentError, "invalid value for Integer(): \"#{str}\"" unless str =~ HEX_RE
|
|
93
|
+
Integer(str, 16)
|
|
94
|
+
end
|
|
95
|
+
else
|
|
96
|
+
# @private
|
|
97
|
+
def from_s(str)
|
|
98
|
+
str = str.gsub(HYPHEN, EMPTY_STRING)
|
|
99
|
+
raise ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
|
|
100
|
+
Integer(str, 16)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|