cassandra-driver 3.0.0.rc.2-java → 3.0.2-java

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