cassandra-driver 3.0.0.rc.2-java → 3.0.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -39
  3. data/lib/cassandra.rb +74 -32
  4. data/lib/cassandra/auth.rb +3 -1
  5. data/lib/cassandra/cluster.rb +14 -3
  6. data/lib/cassandra/cluster/client.rb +98 -62
  7. data/lib/cassandra/cluster/connector.rb +7 -9
  8. data/lib/cassandra/cluster/metadata.rb +1 -1
  9. data/lib/cassandra/cluster/options.rb +19 -7
  10. data/lib/cassandra/cluster/schema/cql_type_parser.rb +3 -0
  11. data/lib/cassandra/cluster/schema/fetchers.rb +1 -1
  12. data/lib/cassandra/custom_data.rb +51 -0
  13. data/lib/cassandra/driver.rb +23 -20
  14. data/lib/cassandra/execution/options.rb +1 -1
  15. data/lib/cassandra/index.rb +22 -8
  16. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +19 -1
  17. data/lib/cassandra/load_balancing/policies/round_robin.rb +7 -0
  18. data/lib/cassandra/load_balancing/policies/token_aware.rb +7 -0
  19. data/lib/cassandra/load_balancing/policies/white_list.rb +7 -0
  20. data/lib/cassandra/protocol.rb +7 -0
  21. data/lib/cassandra/protocol/coder.rb +28 -8
  22. data/lib/cassandra/protocol/cql_byte_buffer.rb +21 -4
  23. data/lib/cassandra/protocol/cql_protocol_handler.rb +3 -2
  24. data/lib/cassandra/protocol/requests/batch_request.rb +1 -1
  25. data/lib/cassandra/protocol/requests/execute_request.rb +1 -1
  26. data/lib/cassandra/protocol/requests/query_request.rb +1 -1
  27. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +4 -2
  28. data/lib/cassandra/protocol/v1.rb +2 -1
  29. data/lib/cassandra/protocol/v3.rb +2 -1
  30. data/lib/cassandra/protocol/v4.rb +5 -3
  31. data/lib/cassandra/result.rb +2 -2
  32. data/lib/cassandra/session.rb +10 -15
  33. data/lib/cassandra/statement.rb +5 -0
  34. data/lib/cassandra/statements/batch.rb +6 -0
  35. data/lib/cassandra/statements/bound.rb +5 -0
  36. data/lib/cassandra/statements/prepared.rb +11 -2
  37. data/lib/cassandra/statements/simple.rb +5 -0
  38. data/lib/cassandra/statements/void.rb +5 -0
  39. data/lib/cassandra/table.rb +5 -5
  40. data/lib/cassandra/timestamp_generator.rb +37 -0
  41. data/lib/cassandra/timestamp_generator/simple.rb +38 -0
  42. data/lib/cassandra/timestamp_generator/ticking_on_duplicate.rb +58 -0
  43. data/lib/cassandra/tuple.rb +2 -2
  44. data/lib/cassandra/types.rb +2 -2
  45. data/lib/cassandra/util.rb +136 -2
  46. data/lib/cassandra/version.rb +1 -1
  47. data/lib/cassandra_murmur3.jar +0 -0
  48. metadata +8 -4
@@ -134,7 +134,8 @@ module Cassandra
134
134
  @connection_options.compressor,
135
135
  @connection_options.heartbeat_interval,
136
136
  @connection_options.idle_timeout,
137
- @connection_options.requests_per_connection)
137
+ @connection_options.requests_per_connection,
138
+ @connection_options.custom_type_handlers)
138
139
  end.flat_map do |connection|
139
140
  # connection is a CqlProtocolHandler
140
141
  f = request_options(connection)
@@ -154,7 +155,7 @@ module Cassandra
154
155
  supported_cql_versions.first :
155
156
  '3.1.0'
156
157
 
157
- startup_connection(connection, cql_version, compression)
158
+ startup_connection(host, connection, cql_version, compression)
158
159
  end
159
160
  f.fallback do |error|
160
161
  case error
@@ -200,7 +201,7 @@ module Cassandra
200
201
  end
201
202
  end
202
203
 
203
- def startup_connection(connection, cql_version, compression)
204
+ def startup_connection(host, connection, cql_version, compression)
204
205
  connection.send_request(Protocol::StartupRequest.new(cql_version, compression),
205
206
  @execution_options.timeout).flat_map do |r|
206
207
  case r
@@ -213,12 +214,9 @@ module Cassandra
213
214
  Ione::Future.failed(cannot_authenticate_error)
214
215
  end
215
216
  else
216
- authenticator = @connection_options.create_authenticator(
217
- r.authentication_class)
217
+ authenticator = @connection_options.create_authenticator(r.authentication_class, host)
218
218
  if authenticator
219
- challenge_response_cycle(connection,
220
- authenticator,
221
- authenticator.initial_response)
219
+ challenge_response_cycle(connection, authenticator, authenticator.initial_response)
222
220
  else
223
221
  Ione::Future.failed(cannot_authenticate_error)
224
222
  end
@@ -283,7 +281,7 @@ module Cassandra
283
281
  case r
284
282
  when Protocol::AuthChallengeResponse
285
283
  token = authenticator.challenge_response(r.token)
286
- challenge_response_cycle(pending_connection, authenticator, token)
284
+ challenge_response_cycle(connection, authenticator, token)
287
285
  when Protocol::AuthSuccessResponse
288
286
  begin
289
287
  authenticator.authentication_successful(r.token)
@@ -61,7 +61,7 @@ module Cassandra
61
61
  end
62
62
 
63
63
  def update(data)
64
- @name = data['name']
64
+ @name = data['cluster_name']
65
65
  @partitioner = @partitioners[data['partitioner']]
66
66
 
67
67
  self
@@ -24,8 +24,8 @@ module Cassandra
24
24
 
25
25
  attr_reader :auth_provider, :compressor, :connect_timeout, :credentials,
26
26
  :heartbeat_interval, :idle_timeout, :port, :schema_refresh_delay,
27
- :schema_refresh_timeout, :ssl
28
- attr_boolean :protocol_negotiable, :synchronize_schema, :client_timestamps, :nodelay
27
+ :schema_refresh_timeout, :ssl, :custom_type_handlers
28
+ attr_boolean :protocol_negotiable, :synchronize_schema, :nodelay
29
29
 
30
30
  attr_accessor :protocol_version
31
31
 
@@ -44,9 +44,9 @@ module Cassandra
44
44
  synchronize_schema,
45
45
  schema_refresh_delay,
46
46
  schema_refresh_timeout,
47
- client_timestamps,
48
47
  nodelay,
49
- requests_per_connection)
48
+ requests_per_connection,
49
+ custom_types)
50
50
  @logger = logger
51
51
  @protocol_version = protocol_version
52
52
  @credentials = credentials
@@ -60,8 +60,11 @@ module Cassandra
60
60
  @synchronize_schema = synchronize_schema
61
61
  @schema_refresh_delay = schema_refresh_delay
62
62
  @schema_refresh_timeout = schema_refresh_timeout
63
- @client_timestamps = client_timestamps
64
63
  @nodelay = nodelay
64
+ @custom_type_handlers = {}
65
+ custom_types.each do |type_klass|
66
+ @custom_type_handlers[type_klass.type] = type_klass
67
+ end
65
68
 
66
69
  @connections_per_local_node = connections_per_local_node
67
70
  @connections_per_remote_node = connections_per_remote_node
@@ -80,8 +83,17 @@ module Cassandra
80
83
  @compressor && @compressor.algorithm
81
84
  end
82
85
 
83
- def create_authenticator(authentication_class)
84
- @auth_provider && @auth_provider.create_authenticator(authentication_class)
86
+ def create_authenticator(authentication_class, host)
87
+ if @auth_provider
88
+ # Auth providers should take an auth-class and host, but they used to not, so for backward compatibility
89
+ # we figure out if this provider does, and if so send both args, otherwise just send the auth-class.
90
+
91
+ if @auth_provider.method(:create_authenticator).arity == 1
92
+ @auth_provider.create_authenticator(authentication_class)
93
+ else
94
+ @auth_provider.create_authenticator(authentication_class, host)
95
+ end
96
+ end
85
97
  end
86
98
 
87
99
  def connections_per_local_node
@@ -72,6 +72,9 @@ module Cassandra
72
72
  Cassandra::Types.tuple(*node.children.map { |t| lookup_type(t, types)})
73
73
  when 'empty' then
74
74
  Cassandra::Types.custom('org.apache.cassandra.db.marshal.EmptyType')
75
+ when /\A'/ then
76
+ # Custom type.
77
+ Cassandra::Types.custom(node.name[1..-2])
75
78
  else
76
79
  types.fetch(node.name) do
77
80
  raise IncompleteTypeError, "unable to lookup type #{node.name.inspect}"
@@ -836,7 +836,7 @@ module Cassandra
836
836
  initial_state = Util.encode_object(
837
837
  Protocol::Coder.read_value_v4(
838
838
  Protocol::CqlByteBuffer.new.append_bytes(aggregate_data['initcond']),
839
- state_type))
839
+ state_type, nil))
840
840
 
841
841
  # The state-function takes arguments: first the stype, then the args of the aggregate.
842
842
  state_function = functions.get(aggregate_data['state_func'],
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2016 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
+ # Use this module to mark domain object classes as custom type implementations for custom-type
20
+ # columns in C*. This module has no logic of its own, but indicates that the marked class has
21
+ # certain methods.
22
+ # @private
23
+ module Cassandra::CustomData
24
+ def self.included base
25
+ base.send :include, InstanceMethods
26
+ base.extend ClassMethods
27
+ end
28
+
29
+ module ClassMethods
30
+ # @return [Cassandra::Types::Custom] the custom type that this class represents.
31
+ def type
32
+ raise NotImplementedError, "#{self.class} must implement the :type class method"
33
+ end
34
+
35
+ # Deserialize the given data into an instance of this domain object class.
36
+ # @param data [String] byte-array representation of a column value of this custom type.
37
+ # @return An instance of the domain object class.
38
+ # @raise [Cassandra::Errors::DecodingError] upon failure.
39
+ def deserialize(data)
40
+ raise NotImplementedError, "#{self.class} must implement the :deserialize class method"
41
+ end
42
+ end
43
+
44
+ module InstanceMethods
45
+ # Serialize this domain object into a byte array to send to C*.
46
+ # @return [String] byte-array representation of this domain object.
47
+ def serialize
48
+ raise NotImplementedError, "#{self.class} must implement the :serialize instance method"
49
+ end
50
+ end
51
+ end
@@ -92,22 +92,25 @@ module Cassandra
92
92
  schema_fetcher)
93
93
  end
94
94
 
95
+ let(:cluster_klass) { Cluster }
96
+
95
97
  let(:cluster) do
96
- Cluster.new(logger,
97
- io_reactor,
98
- executor,
99
- control_connection,
100
- cluster_registry,
101
- cluster_schema,
102
- cluster_metadata,
103
- execution_options,
104
- connection_options,
105
- load_balancing_policy,
106
- reconnection_policy,
107
- retry_policy,
108
- address_resolution_policy,
109
- connector,
110
- futures_factory)
98
+ cluster_klass.new(logger,
99
+ io_reactor,
100
+ executor,
101
+ control_connection,
102
+ cluster_registry,
103
+ cluster_schema,
104
+ cluster_metadata,
105
+ execution_options,
106
+ connection_options,
107
+ load_balancing_policy,
108
+ reconnection_policy,
109
+ retry_policy,
110
+ address_resolution_policy,
111
+ connector,
112
+ futures_factory,
113
+ timestamp_generator)
111
114
  end
112
115
 
113
116
  let(:execution_options) do
@@ -135,12 +138,13 @@ module Cassandra
135
138
  synchronize_schema,
136
139
  schema_refresh_delay,
137
140
  schema_refresh_timeout,
138
- client_timestamps,
139
141
  nodelay,
140
- requests_per_connection
142
+ requests_per_connection,
143
+ custom_types
141
144
  )
142
145
  end
143
146
 
147
+ let(:custom_types) { [] }
144
148
  let(:port) { 9042 }
145
149
  let(:protocol_version) { nil }
146
150
  let(:connect_timeout) { 10 }
@@ -165,15 +169,14 @@ module Cassandra
165
169
  let(:page_size) { 10000 }
166
170
  let(:heartbeat_interval) { 30 }
167
171
  let(:idle_timeout) { 60 }
168
- let(:timeout) { 10 }
172
+ let(:timeout) { 12 }
169
173
  let(:synchronize_schema) { true }
170
174
  let(:schema_refresh_delay) { 1 }
171
175
  let(:schema_refresh_timeout) { 10 }
172
176
  let(:thread_pool_size) { 4 }
173
177
  let(:shuffle_replicas) { true }
174
- let(:client_timestamps) { false }
175
178
  let(:nodelay) { true }
176
-
179
+ let(:timestamp_generator) { nil }
177
180
  let(:connections_per_local_node) { nil }
178
181
  let(:connections_per_remote_node) { nil }
179
182
  let(:requests_per_connection) { nil }
@@ -177,7 +177,7 @@ module Cassandra
177
177
  @consistency = consistency || trusted_options.consistency
178
178
  @page_size = page_size || trusted_options.page_size
179
179
  @trace = trace.nil? ? trusted_options.trace? : !!trace
180
- @timeout = timeout || trusted_options.timeout
180
+ @timeout = options.key?(:timeout) ? timeout : trusted_options.timeout
181
181
  @serial_consistency = serial_consistency || trusted_options.serial_consistency
182
182
  @arguments = arguments || trusted_options.arguments
183
183
  @type_hints = type_hints || trusted_options.type_hints
@@ -39,8 +39,17 @@ module Cassandra
39
39
  @table = table
40
40
  @name = name.freeze
41
41
  @kind = kind
42
- @target = target.freeze
43
42
  @options = options.freeze
43
+
44
+ # Target is a bit tricky; it may be an escaped name or not
45
+ # depending on C* version. Unify to be unescaped since a user
46
+ # who wants to know the target would want the bare column name.
47
+
48
+ @target = if target[0] == '"'
49
+ target[1..-2]
50
+ else
51
+ target
52
+ end.freeze
44
53
  end
45
54
 
46
55
  # @return [Boolean] whether or not this index uses a custom class.
@@ -57,13 +66,18 @@ module Cassandra
57
66
  def to_cql
58
67
  keyspace_name = Util.escape_name(@table.keyspace.name)
59
68
  table_name = Util.escape_name(@table.name)
60
- index_name = Util.escape_name(name)
69
+ index_name = Util.escape_name(@name)
70
+
71
+ # Target is interesting in that it's not necessarily a column name,
72
+ # so we can't simply escape it. If it contains a paren, we take it as is,
73
+ # otherwise assume it's a column name and escape accordingly.
74
+ escaped_target = @target.include?('(') ? @target : Util.escape_name(@target)
61
75
 
62
76
  if custom_index?
63
- "CREATE CUSTOM INDEX #{index_name} ON #{keyspace_name}.#{table_name} (#{target}) " \
64
- "USING '#{@options['class_name']}' #{options_cql};"
77
+ "CREATE CUSTOM INDEX #{index_name} ON #{keyspace_name}.#{table_name} (#{escaped_target}) " \
78
+ "USING '#{@options['class_name']}'#{options_cql};"
65
79
  else
66
- "CREATE INDEX #{index_name} ON #{keyspace_name}.#{table_name} (#{target});"
80
+ "CREATE INDEX #{index_name} ON #{keyspace_name}.#{table_name} (#{escaped_target});"
67
81
  end
68
82
  end
69
83
 
@@ -81,7 +95,7 @@ module Cassandra
81
95
  # @private
82
96
  def inspect
83
97
  "#<#{self.class.name}:0x#{object_id.to_s(16)} " \
84
- "@name=#{@name} table_name=#{@table.name} kind=#{@kind} target=#{@target}>"
98
+ "@name=#{@name.inspect} @table=#{@table.inspect} @kind=#{@kind.inspect} @target=#{@target.inspect}>"
85
99
  end
86
100
 
87
101
  private
@@ -93,9 +107,9 @@ module Cassandra
93
107
  end
94
108
  return '' if filtered_options.empty?
95
109
 
96
- result = 'WITH OPTIONS = {'
110
+ result = ' WITH OPTIONS = {'
97
111
  result << filtered_options.map do |key, value|
98
- "'#{key}':'#{value}'"
112
+ "'#{key}': '#{value}'"
99
113
  end.join(', ')
100
114
  result << '}'
101
115
  result
@@ -62,7 +62,7 @@ module Cassandra
62
62
  include MonitorMixin
63
63
 
64
64
  def initialize(datacenter = nil,
65
- max_remote_hosts_to_use = nil,
65
+ max_remote_hosts_to_use = 0,
66
66
  use_remote_hosts_for_local_consistency = false)
67
67
  datacenter &&= String(datacenter)
68
68
  max_remote_hosts_to_use &&= Integer(max_remote_hosts_to_use)
@@ -75,6 +75,13 @@ module Cassandra
75
75
  end
76
76
  end
77
77
 
78
+ # If use_remote* is true, max_remote* must be > 0
79
+ if use_remote_hosts_for_local_consistency
80
+ Util.assert(max_remote_hosts_to_use.nil? || max_remote_hosts_to_use > 0,
81
+ 'max_remote_hosts_to_use must be nil (meaning unlimited) or > 0 when ' \
82
+ 'use_remote_hosts_for_local_consistency is true')
83
+ end
84
+
78
85
  @datacenter = datacenter
79
86
  @max_remote = max_remote_hosts_to_use
80
87
  @local = ::Array.new
@@ -148,6 +155,17 @@ module Cassandra
148
155
 
149
156
  Plan.new(local, remote, position)
150
157
  end
158
+
159
+ # @private
160
+ def inspect
161
+ "#<#{self.class.name}:0x#{object_id.to_s(16)} " \
162
+ "datacenter=#{@datacenter.inspect}, " \
163
+ "use_remote=#{@use_remote.inspect}, " \
164
+ "max_remote=#{@max_remote.inspect}, " \
165
+ "local=#{@local.inspect}, " \
166
+ "remote=#{@remote.inspect}, " \
167
+ "position=#{@position.inspect}>"
168
+ end
151
169
  end
152
170
  end
153
171
  end
@@ -128,6 +128,13 @@ module Cassandra
128
128
 
129
129
  Plan.new(hosts, position)
130
130
  end
131
+
132
+ # @private
133
+ def inspect
134
+ "#<#{self.class.name}:0x#{object_id.to_s(16)} " \
135
+ "hosts=#{@hosts.inspect}, " \
136
+ "position=#{@position.inspect}>"
137
+ end
131
138
  end
132
139
  end
133
140
  end
@@ -136,6 +136,13 @@ module Cassandra
136
136
 
137
137
  Plan.new(replicas, @policy, keyspace, statement, options)
138
138
  end
139
+
140
+ # @private
141
+ def inspect
142
+ "#<#{self.class.name}:0x#{object_id.to_s(16)} " \
143
+ "policy=#{@policy.inspect}, " \
144
+ "shuffle=#{@shuffle.inspect}>"
145
+ end
139
146
  end
140
147
  end
141
148
  end
@@ -87,6 +87,13 @@ module Cassandra
87
87
  def host_down(host)
88
88
  @policy.host_down(host) if @ips.include?(host.ip)
89
89
  end
90
+
91
+ # @private
92
+ def inspect
93
+ "#<#{self.class.name}:0x#{object_id.to_s(16)} " \
94
+ "policy=#{@policy.inspect}, " \
95
+ "ips=#{@ips.inspect}>"
96
+ end
90
97
  end
91
98
  end
92
99
  end
@@ -28,6 +28,13 @@ module Cassandra
28
28
 
29
29
  BYTES_FORMAT = 'C*'.freeze
30
30
  TWO_INTS_FORMAT = 'NN'.freeze
31
+
32
+ # All of the formats above are big-endian (e.g. network-byte-order). Some payloads (custom types) may have
33
+ # little-endian components.
34
+
35
+ DOUBLE_FORMAT_LE = 'E'.freeze
36
+ INT_FORMAT_LE = 'V'.freeze
37
+ SHORT_FORMAT_LE = 'v'.freeze
31
38
  end
32
39
 
33
40
  module Constants
@@ -97,6 +97,7 @@ module Cassandra
97
97
  when :bigint, :counter then write_bigint(buffer, value)
98
98
  when :blob then write_blob(buffer, value)
99
99
  when :boolean then write_boolean(buffer, value)
100
+ when :custom then write_custom(buffer, value, type)
100
101
  when :decimal then write_decimal(buffer, value)
101
102
  when :double then write_double(buffer, value)
102
103
  when :float then write_float(buffer, value)
@@ -221,19 +222,19 @@ module Cassandra
221
222
  end
222
223
  end
223
224
 
224
- def read_values_v4(buffer, column_metadata)
225
+ def read_values_v4(buffer, column_metadata, custom_type_handlers)
225
226
  ::Array.new(buffer.read_int) do |_i|
226
227
  row = ::Hash.new
227
228
 
228
229
  column_metadata.each do |(_, _, column, type)|
229
- row[column] = read_value_v4(buffer, type)
230
+ row[column] = read_value_v4(buffer, type, custom_type_handlers)
230
231
  end
231
232
 
232
233
  row
233
234
  end
234
235
  end
235
236
 
236
- def read_value_v4(buffer, type)
237
+ def read_value_v4(buffer, type, custom_type_handlers)
237
238
  case type.kind
238
239
  when :ascii then read_ascii(buffer)
239
240
  when :bigint, :counter then read_bigint(buffer)
@@ -253,11 +254,12 @@ module Cassandra
253
254
  when :smallint then read_smallint(buffer)
254
255
  when :time then read_time(buffer)
255
256
  when :date then read_date(buffer)
257
+ when :custom then read_custom(buffer, type, custom_type_handlers)
256
258
  when :list
257
259
  return nil unless read_size(buffer)
258
260
 
259
261
  value_type = type.value_type
260
- ::Array.new(buffer.read_signed_int) { read_value_v4(buffer, value_type) }
262
+ ::Array.new(buffer.read_signed_int) { read_value_v4(buffer, value_type, custom_type_handlers) }
261
263
  when :map
262
264
  return nil unless read_size(buffer)
263
265
 
@@ -266,7 +268,7 @@ module Cassandra
266
268
  value = ::Hash.new
267
269
 
268
270
  buffer.read_signed_int.times do
269
- value[read_value_v4(buffer, key_type)] = read_value_v4(buffer, value_type)
271
+ value[read_value_v4(buffer, key_type, custom_type_handlers)] = read_value_v4(buffer, value_type, custom_type_handlers)
270
272
  end
271
273
 
272
274
  value
@@ -277,7 +279,7 @@ module Cassandra
277
279
  value = ::Set.new
278
280
 
279
281
  buffer.read_signed_int.times do
280
- value << read_value_v4(buffer, value_type)
282
+ value << read_value_v4(buffer, value_type, custom_type_handlers)
281
283
  end
282
284
 
283
285
  value
@@ -295,7 +297,7 @@ module Cassandra
295
297
  values[field.name] = if length - buffer.length >= size
296
298
  nil
297
299
  else
298
- read_value_v4(buffer, field.type)
300
+ read_value_v4(buffer, field.type, custom_type_handlers)
299
301
  end
300
302
  end
301
303
 
@@ -308,7 +310,7 @@ module Cassandra
308
310
 
309
311
  members.each do |member_type|
310
312
  break if buffer.empty?
311
- values << read_value_v4(buffer, member_type)
313
+ values << read_value_v4(buffer, member_type, custom_type_handlers)
312
314
  end
313
315
 
314
316
  values.fill(nil, values.length, (members.length - values.length))
@@ -774,6 +776,15 @@ module Cassandra
774
776
  read_size(buffer) && buffer.read(1) == Constants::TRUE_BYTE
775
777
  end
776
778
 
779
+ def read_custom(buffer, type, custom_type_handlers)
780
+ # Lookup the type-name to get the Class that can deserialize buffer data into a custom domain object.
781
+ unless custom_type_handlers && custom_type_handlers.key?(type)
782
+ raise Errors::DecodingError, %(Unsupported custom column type: #{type.name})
783
+ end
784
+ num_bytes = read_size(buffer)
785
+ custom_type_handlers[type].deserialize(buffer.read(num_bytes)) if num_bytes && num_bytes > 0
786
+ end
787
+
777
788
  def read_decimal(buffer)
778
789
  size = read_size(buffer)
779
790
  size && buffer.read_decimal(size)
@@ -860,6 +871,15 @@ module Cassandra
860
871
  buffer.append(value ? Constants::TRUE_BYTE : Constants::FALSE_BYTE)
861
872
  end
862
873
 
874
+ def write_custom(buffer, value, type)
875
+ # Verify that the given type-name matches the value's cql type name.
876
+ if value.class.type != type
877
+ raise Errors::EncodingError, "type mismatch: value is a #{value.type} and column is a #{type}"
878
+ end
879
+
880
+ buffer.append_bytes(value.serialize)
881
+ end
882
+
863
883
  def write_decimal(buffer, value)
864
884
  buffer.append_bytes(CqlByteBuffer.new.append_decimal(value))
865
885
  end