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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -1
  3. data/lib/cassandra.rb +74 -55
  4. data/lib/cassandra/attr_boolean.rb +33 -0
  5. data/lib/cassandra/auth.rb +2 -1
  6. data/lib/cassandra/auth/providers/password.rb +4 -16
  7. data/lib/cassandra/cluster/connector.rb +14 -4
  8. data/lib/cassandra/cluster/control_connection.rb +59 -67
  9. data/lib/cassandra/cluster/metadata.rb +1 -3
  10. data/lib/cassandra/cluster/options.rb +9 -10
  11. data/lib/cassandra/cluster/registry.rb +16 -5
  12. data/lib/cassandra/cluster/schema.rb +45 -1
  13. data/lib/cassandra/cluster/schema/fetchers.rb +475 -272
  14. data/lib/cassandra/cluster/schema/fqcn_type_parser.rb +2 -6
  15. data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +5 -7
  16. data/lib/cassandra/column.rb +1 -20
  17. data/lib/cassandra/column_container.rb +322 -0
  18. data/lib/cassandra/compression/compressors/lz4.rb +3 -5
  19. data/lib/cassandra/driver.rb +1 -1
  20. data/lib/cassandra/errors.rb +38 -22
  21. data/lib/cassandra/execution/options.rb +4 -2
  22. data/lib/cassandra/future.rb +3 -9
  23. data/lib/cassandra/host.rb +16 -2
  24. data/lib/cassandra/index.rb +104 -0
  25. data/lib/cassandra/keyspace.rb +88 -9
  26. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +6 -10
  27. data/lib/cassandra/materialized_view.rb +90 -0
  28. data/lib/cassandra/protocol/coder.rb +3 -3
  29. data/lib/cassandra/protocol/cql_byte_buffer.rb +12 -11
  30. data/lib/cassandra/protocol/cql_protocol_handler.rb +12 -8
  31. data/lib/cassandra/protocol/request.rb +4 -5
  32. data/lib/cassandra/protocol/requests/execute_request.rb +3 -5
  33. data/lib/cassandra/protocol/requests/query_request.rb +1 -1
  34. data/lib/cassandra/protocol/requests/startup_request.rb +6 -8
  35. data/lib/cassandra/protocol/response.rb +1 -2
  36. data/lib/cassandra/protocol/responses/auth_challenge_response.rb +3 -4
  37. data/lib/cassandra/protocol/responses/auth_success_response.rb +3 -4
  38. data/lib/cassandra/protocol/responses/authenticate_response.rb +3 -4
  39. data/lib/cassandra/protocol/responses/error_response.rb +3 -4
  40. data/lib/cassandra/protocol/responses/event_response.rb +2 -3
  41. data/lib/cassandra/protocol/responses/prepared_result_response.rb +3 -4
  42. data/lib/cassandra/protocol/responses/ready_response.rb +3 -4
  43. data/lib/cassandra/protocol/responses/result_response.rb +7 -8
  44. data/lib/cassandra/protocol/responses/rows_result_response.rb +3 -4
  45. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +3 -4
  46. data/lib/cassandra/protocol/responses/schema_change_result_response.rb +3 -4
  47. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +3 -4
  48. data/lib/cassandra/protocol/responses/status_change_event_response.rb +3 -4
  49. data/lib/cassandra/protocol/responses/supported_response.rb +3 -4
  50. data/lib/cassandra/protocol/responses/topology_change_event_response.rb +3 -4
  51. data/lib/cassandra/protocol/responses/void_result_response.rb +3 -4
  52. data/lib/cassandra/protocol/v1.rb +1 -5
  53. data/lib/cassandra/protocol/v3.rb +1 -3
  54. data/lib/cassandra/result.rb +2 -1
  55. data/lib/cassandra/retry/policies/downgrading_consistency.rb +1 -3
  56. data/lib/cassandra/statements/prepared.rb +3 -3
  57. data/lib/cassandra/table.rb +39 -220
  58. data/lib/cassandra/time_uuid.rb +5 -7
  59. data/lib/cassandra/tuple.rb +4 -12
  60. data/lib/cassandra/types.rb +92 -65
  61. data/lib/cassandra/udt.rb +34 -14
  62. data/lib/cassandra/uuid.rb +10 -18
  63. data/lib/cassandra/version.rb +1 -1
  64. data/lib/cassandra_murmur3.jar +0 -0
  65. metadata +8 -2
@@ -39,9 +39,7 @@ module Cassandra
39
39
  end
40
40
 
41
41
  def find_replicas(keyspace, statement)
42
- unless statement.respond_to?(:partition_key) && statement.respond_to?(:keyspace)
43
- return EMPTY_LIST
44
- end
42
+ return EMPTY_LIST unless statement.respond_to?(:partition_key) && statement.respond_to?(:keyspace)
45
43
 
46
44
  keyspace = String(statement.keyspace || keyspace)
47
45
  partition_key = statement.partition_key
@@ -20,9 +20,12 @@ module Cassandra
20
20
  class Cluster
21
21
  # @private
22
22
  class Options
23
+ extend AttrBoolean
24
+
23
25
  attr_reader :auth_provider, :compressor, :connect_timeout, :credentials,
24
26
  :heartbeat_interval, :idle_timeout, :port, :schema_refresh_delay,
25
27
  :schema_refresh_timeout, :ssl
28
+ attr_boolean :protocol_negotiable, :synchronize_schema, :client_timestamps, :nodelay
26
29
 
27
30
  attr_accessor :protocol_version
28
31
 
@@ -63,18 +66,14 @@ module Cassandra
63
66
  @connections_per_local_node = connections_per_local_node
64
67
  @connections_per_remote_node = connections_per_remote_node
65
68
  @requests_per_connection = requests_per_connection
66
- end
67
69
 
68
- def synchronize_schema?
69
- @synchronize_schema
70
- end
71
-
72
- def client_timestamps?
73
- @client_timestamps
74
- end
70
+ # If @protocol_version is nil, it means we want the driver to negotiate the
71
+ # protocol starting with our known max (4). If @protocol_version is not nil,
72
+ # it means the user wants us to use a particular version, so we should not
73
+ # support negotiation.
75
74
 
76
- def nodelay?
77
- @nodelay
75
+ @protocol_negotiable = @protocol_version.nil?
76
+ @protocol_version ||= 4
78
77
  end
79
78
 
80
79
  def compression
@@ -76,7 +76,9 @@ module Cassandra
76
76
  if host.id == data['host_id'] &&
77
77
  host.release_version == data['release_version'] &&
78
78
  host.rack == data['rack'] &&
79
- host.datacenter == data['data_center']
79
+ host.datacenter == data['data_center'] &&
80
+ host.broadcast_address == data['broadcast_address'] &&
81
+ host.listen_address == data['listen_address']
80
82
 
81
83
  return self if host.up?
82
84
 
@@ -166,7 +168,10 @@ module Cassandra
166
168
  data['data_center'],
167
169
  data['release_version'],
168
170
  Array(data['tokens']).freeze,
169
- :up)
171
+ :up,
172
+ data['broadcast_address'],
173
+ data['listen_address']
174
+ )
170
175
  end
171
176
 
172
177
  def toggle_up(host)
@@ -176,7 +181,9 @@ module Cassandra
176
181
  host.datacenter,
177
182
  host.release_version,
178
183
  host.tokens,
179
- :up)
184
+ :up,
185
+ host.broadcast_address,
186
+ host.listen_address)
180
187
  @logger.debug("Host #{host.ip} is up")
181
188
  @listeners.each do |listener|
182
189
  begin
@@ -195,7 +202,9 @@ module Cassandra
195
202
  host.datacenter,
196
203
  host.release_version,
197
204
  host.tokens,
198
- :down)
205
+ :down,
206
+ host.broadcast_address,
207
+ host.listen_address)
199
208
  @logger.debug("Host #{host.ip} is down")
200
209
  @listeners.reverse_each do |listener|
201
210
  begin
@@ -216,7 +225,9 @@ module Cassandra
216
225
  host.datacenter,
217
226
  host.release_version,
218
227
  host.tokens,
219
- :down)
228
+ :down,
229
+ host.broadcast_address,
230
+ host.listen_address)
220
231
  @listeners.reverse_each do |listener|
221
232
  begin
222
233
  listener.host_down(host)
@@ -126,7 +126,7 @@ module Cassandra
126
126
  end
127
127
 
128
128
  def replace_table(table)
129
- keyspace = @keyspaces[table.keyspace]
129
+ keyspace = table.keyspace
130
130
 
131
131
  return self unless keyspace
132
132
 
@@ -169,6 +169,50 @@ module Cassandra
169
169
  self
170
170
  end
171
171
 
172
+ def replace_materialized_view(view)
173
+ keyspace = view.keyspace
174
+
175
+ return self unless keyspace
176
+
177
+ old_view = keyspace.materialized_view(view.name)
178
+
179
+ return self if old_view == view
180
+
181
+ keyspace = keyspace.update_materialized_view(view)
182
+
183
+ synchronize do
184
+ keyspaces = @keyspaces.dup
185
+ keyspaces[keyspace.name] = keyspace
186
+ @keyspaces = keyspaces
187
+ end
188
+
189
+ keyspace_changed(keyspace)
190
+
191
+ self
192
+ end
193
+
194
+ def delete_materialized_view(keyspace_name, view_name)
195
+ keyspace = @keyspaces[keyspace_name]
196
+
197
+ return self unless keyspace
198
+
199
+ view = keyspace.materialized_view(view_name)
200
+
201
+ return self unless view
202
+
203
+ keyspace = keyspace.delete_materialized_view(view_name)
204
+
205
+ synchronize do
206
+ keyspaces = @keyspaces.dup
207
+ keyspaces[keyspace_name] = keyspace
208
+ @keyspaces = keyspaces
209
+ end
210
+
211
+ keyspace_changed(keyspace)
212
+
213
+ self
214
+ end
215
+
172
216
  def replace_type(type)
173
217
  keyspace = @keyspaces[type.keyspace]
174
218
 
@@ -26,20 +26,23 @@ module Cassandra
26
26
  COMPRESSION_PACKAGE_PREFIX = 'org.apache.cassandra.io.compress.'.freeze
27
27
 
28
28
  def fetch(connection)
29
+ # rubocop:disable Metrics/LineLength
29
30
  Ione::Future.all(select_keyspaces(connection),
30
31
  select_tables(connection),
31
32
  select_columns(connection),
32
33
  select_types(connection),
33
34
  select_functions(connection),
34
- select_aggregates(connection))
35
- .map do |(rows_keyspaces, rows_tables, rows_columns,
36
- rows_types, rows_functions, rows_aggregates)|
37
-
35
+ select_aggregates(connection),
36
+ select_materialized_views(connection),
37
+ select_indexes(connection))
38
+ .map do |rows_keyspaces, rows_tables, rows_columns, rows_types, rows_functions, rows_aggregates, rows_views, rows_indexes|
38
39
  lookup_tables = map_rows_by(rows_tables, 'keyspace_name')
39
40
  lookup_columns = map_rows_by(rows_columns, 'keyspace_name')
40
41
  lookup_types = map_rows_by(rows_types, 'keyspace_name')
41
42
  lookup_functions = map_rows_by(rows_functions, 'keyspace_name')
42
43
  lookup_aggregates = map_rows_by(rows_aggregates, 'keyspace_name')
44
+ lookup_views = map_rows_by(rows_views, 'keyspace_name')
45
+ lookup_indexes = map_rows_by(rows_indexes, 'keyspace_name')
43
46
 
44
47
  rows_keyspaces.map do |keyspace_data|
45
48
  name = keyspace_data['keyspace_name']
@@ -49,7 +52,9 @@ module Cassandra
49
52
  lookup_columns[name],
50
53
  lookup_types[name],
51
54
  lookup_functions[name],
52
- lookup_aggregates[name])
55
+ lookup_aggregates[name],
56
+ lookup_views[name],
57
+ lookup_indexes[name])
53
58
  end
54
59
  end
55
60
  end
@@ -60,9 +65,10 @@ module Cassandra
60
65
  select_keyspace_columns(connection, keyspace_name),
61
66
  select_keyspace_types(connection, keyspace_name),
62
67
  select_keyspace_functions(connection, keyspace_name),
63
- select_keyspace_aggregates(connection, keyspace_name))
64
- .map do |(rows_keyspaces, rows_tables, rows_columns,
65
- rows_types, rows_functions, rows_aggregates)|
68
+ select_keyspace_aggregates(connection, keyspace_name),
69
+ select_keyspace_materialized_views(connection, keyspace_name),
70
+ select_keyspace_indexes(connection, keyspace_name))
71
+ .map do |rows_keyspaces, rows_tables, rows_columns, rows_types, rows_functions, rows_aggregates, rows_views, rows_indexes|
66
72
  if rows_keyspaces.empty?
67
73
  nil
68
74
  else
@@ -71,20 +77,40 @@ module Cassandra
71
77
  rows_columns,
72
78
  rows_types,
73
79
  rows_functions,
74
- rows_aggregates)
80
+ rows_aggregates,
81
+ rows_views,
82
+ rows_indexes)
75
83
  end
76
84
  end
77
85
  end
78
86
 
79
87
  def fetch_table(connection, keyspace_name, table_name)
80
88
  Ione::Future.all(select_table(connection, keyspace_name, table_name),
81
- select_table_columns(connection, keyspace_name, table_name))
82
- .map do |(rows_tables, rows_columns)|
89
+ select_table_columns(connection, keyspace_name, table_name),
90
+ select_table_indexes(connection, keyspace_name, table_name))
91
+ .map do |(rows_tables, rows_columns, rows_indexes)|
83
92
  if rows_tables.empty?
84
93
  nil
85
94
  else
86
95
  create_table(rows_tables.first,
87
- rows_columns)
96
+ rows_columns,
97
+ rows_indexes)
98
+ end
99
+ end
100
+ end
101
+
102
+ def fetch_materialized_view(connection, keyspace_name, view_name)
103
+ Ione::Future.all(select_materialized_view(connection, keyspace_name, view_name),
104
+ select_table_columns(connection, keyspace_name, view_name))
105
+ .map do |rows_views, rows_columns|
106
+ if rows_views.empty?
107
+ nil
108
+ else
109
+ view_row = rows_views.first
110
+ base_table = @schema.keyspace(keyspace_name).table(view_row['base_table_name'])
111
+ create_materialized_view(view_row,
112
+ rows_columns,
113
+ base_table)
88
114
  end
89
115
  end
90
116
  end
@@ -100,8 +126,8 @@ module Cassandra
100
126
  end
101
127
 
102
128
  def fetch_function(connection, keyspace_name, function_name, function_args)
103
- select_function(connection, keyspace_name, function_name, function_args).
104
- map do |rows_functions|
129
+ select_function(connection, keyspace_name, function_name, function_args)
130
+ .map do |rows_functions|
105
131
  if rows_functions.empty?
106
132
  nil
107
133
  else
@@ -111,13 +137,13 @@ module Cassandra
111
137
  end
112
138
 
113
139
  def fetch_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
114
- select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args).
115
- map do |rows_aggregates|
140
+ select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
141
+ .map do |rows_aggregates|
116
142
  if rows_aggregates.empty?
117
143
  nil
118
144
  else
119
- create_aggregate(rows_aggregates.first, @schema.keyspace(keyspace_name).
120
- send(:raw_functions))
145
+ create_aggregate(rows_aggregates.first, @schema.keyspace(keyspace_name)
146
+ .send(:raw_functions))
121
147
  end
122
148
  end
123
149
  end
@@ -132,10 +158,18 @@ module Cassandra
132
158
  FUTURE_EMPTY_LIST
133
159
  end
134
160
 
161
+ def select_materialized_views(connection)
162
+ FUTURE_EMPTY_LIST
163
+ end
164
+
135
165
  def select_columns(connection)
136
166
  FUTURE_EMPTY_LIST
137
167
  end
138
168
 
169
+ def select_indexes(connection)
170
+ FUTURE_EMPTY_LIST
171
+ end
172
+
139
173
  def select_types(connection)
140
174
  FUTURE_EMPTY_LIST
141
175
  end
@@ -156,10 +190,18 @@ module Cassandra
156
190
  FUTURE_EMPTY_LIST
157
191
  end
158
192
 
193
+ def select_keyspace_materialized_views(connection, keyspace_name)
194
+ FUTURE_EMPTY_LIST
195
+ end
196
+
159
197
  def select_keyspace_columns(connection, keyspace_name)
160
198
  FUTURE_EMPTY_LIST
161
199
  end
162
200
 
201
+ def select_keyspace_indexes(connection, keyspace_name)
202
+ FUTURE_EMPTY_LIST
203
+ end
204
+
163
205
  def select_keyspace_types(connection, keyspace_name)
164
206
  FUTURE_EMPTY_LIST
165
207
  end
@@ -176,10 +218,18 @@ module Cassandra
176
218
  FUTURE_EMPTY_LIST
177
219
  end
178
220
 
221
+ def select_materialized_view(connection, keyspace_name, view_name)
222
+ FUTURE_EMPTY_LIST
223
+ end
224
+
179
225
  def select_table_columns(connection, keyspace_name, table_name)
180
226
  FUTURE_EMPTY_LIST
181
227
  end
182
228
 
229
+ def select_table_indexes(connection, keyspace_name, table_name)
230
+ FUTURE_EMPTY_LIST
231
+ end
232
+
183
233
  def select_type(connection, keyspace_name, type_name)
184
234
  FUTURE_EMPTY_LIST
185
235
  end
@@ -195,7 +245,7 @@ module Cassandra
195
245
  def send_select_request(connection, cql, params = EMPTY_LIST, types = EMPTY_LIST)
196
246
  backtrace = caller
197
247
  connection.send_request(
198
- Protocol::QueryRequest.new(cql, params, types, :one)).map do |r|
248
+ Protocol::QueryRequest.new(cql, params, types, :one)).map do |r|
199
249
  case r
200
250
  when Protocol::RowsResultResponse
201
251
  r.rows
@@ -212,43 +262,44 @@ module Cassandra
212
262
  def map_rows_by(rows, key_name, &block)
213
263
  rows.each_with_object(::Hash.new { EMPTY_LIST }) do |row, map|
214
264
  key = row[key_name]
215
- map[key] = [] unless map.has_key?(key)
265
+ map[key] = [] unless map.key?(key)
216
266
 
217
- if block
218
- map[key] << yield(row)
219
- else
220
- map[key] << row
221
- end
267
+ map[key] << if block
268
+ yield(row)
269
+ else
270
+ row
271
+ end
222
272
  end
223
273
  end
224
274
  end
225
275
 
226
276
  # @private
227
277
  module Fetchers
278
+ # rubocop:disable Style/ClassAndModuleCamelCase
228
279
  class V1_2_x
229
280
  SELECT_KEYSPACES = 'SELECT * FROM system.schema_keyspaces'.freeze
230
281
  SELECT_TABLES = 'SELECT * FROM system.schema_columnfamilies'.freeze
231
282
  SELECT_COLUMNS = 'SELECT * FROM system.schema_columns'.freeze
232
283
  SELECT_KEYSPACE =
233
- 'SELECT * ' \
234
- 'FROM system.schema_keyspaces ' \
235
- 'WHERE keyspace_name = \'%s\''.freeze
284
+ 'SELECT * ' \
285
+ 'FROM system.schema_keyspaces ' \
286
+ 'WHERE keyspace_name = \'%s\''.freeze
236
287
  SELECT_KEYSPACE_TABLES =
237
- 'SELECT * ' \
238
- 'FROM system.schema_columnfamilies ' \
239
- 'WHERE keyspace_name = \'%s\''.freeze
288
+ 'SELECT * ' \
289
+ 'FROM system.schema_columnfamilies ' \
290
+ 'WHERE keyspace_name = \'%s\''.freeze
240
291
  SELECT_KEYSPACE_COLUMNS =
241
- 'SELECT * FROM system.schema_columns WHERE keyspace_name = \'%s\''.freeze
292
+ 'SELECT * FROM system.schema_columns WHERE keyspace_name = \'%s\''.freeze
242
293
  SELECT_TABLE =
243
- 'SELECT * ' \
244
- 'FROM system.schema_columnfamilies ' \
245
- 'WHERE keyspace_name = \'%s\' AND columnfamily_name = \'%s\''.freeze
294
+ 'SELECT * ' \
295
+ 'FROM system.schema_columnfamilies ' \
296
+ 'WHERE keyspace_name = \'%s\' AND columnfamily_name = \'%s\''.freeze
246
297
  SELECT_TABLE_COLUMNS =
247
- 'SELECT * ' \
248
- 'FROM system.schema_columns ' \
249
- 'WHERE keyspace_name = \'%s\' AND columnfamily_name = \'%s\''.freeze
298
+ 'SELECT * ' \
299
+ 'FROM system.schema_columns ' \
300
+ 'WHERE keyspace_name = \'%s\' AND columnfamily_name = \'%s\''.freeze
250
301
 
251
- include Fetcher
302
+ include Cassandra::Cluster::Schema::Fetcher
252
303
 
253
304
  def initialize(type_parser, schema)
254
305
  @type_parser = type_parser
@@ -274,20 +325,20 @@ module Cassandra
274
325
  end
275
326
 
276
327
  def select_keyspace_tables(connection, keyspace_name)
277
- send_select_request(connection, SELECT_KEYSPACE_TABLES % keyspace_name)
328
+ send_select_request(connection, format(SELECT_KEYSPACE_TABLES, keyspace_name))
278
329
  end
279
330
 
280
331
  def select_keyspace_columns(connection, keyspace_name)
281
- send_select_request(connection, SELECT_KEYSPACE_COLUMNS % keyspace_name)
332
+ send_select_request(connection, format(SELECT_KEYSPACE_COLUMNS, keyspace_name))
282
333
  end
283
334
 
284
335
  def select_table(connection, keyspace_name, table_name)
285
- send_select_request(connection, SELECT_TABLE % [keyspace_name, table_name])
336
+ send_select_request(connection, format(SELECT_TABLE, keyspace_name, table_name))
286
337
  end
287
338
 
288
339
  def select_table_columns(connection, keyspace_name, table_name)
289
340
  send_select_request(connection,
290
- SELECT_TABLE_COLUMNS % [keyspace_name, table_name])
341
+ format(SELECT_TABLE_COLUMNS, keyspace_name, table_name))
291
342
  end
292
343
 
293
344
  def create_replication(keyspace_data)
@@ -298,12 +349,13 @@ module Cassandra
298
349
  end
299
350
 
300
351
  def create_keyspace(keyspace_data, rows_tables, rows_columns,
301
- rows_types, rows_functions, rows_aggregates)
352
+ rows_types, rows_functions, rows_aggregates,
353
+ rows_views, rows_indexes)
302
354
  keyspace_name = keyspace_data['keyspace_name']
303
355
  replication = create_replication(keyspace_data)
304
- types = rows_types.each_with_object({}) do |row, types|
305
- types[row['type_name']] = create_type(row)
306
- end
356
+ types = rows_types.each_with_object({}) do |row, h|
357
+ h[row['type_name']] = create_type(row)
358
+ end
307
359
 
308
360
  # Create a FunctionCollection for the functions and aggregates.
309
361
  functions = Cassandra::FunctionCollection.new
@@ -317,9 +369,10 @@ module Cassandra
317
369
  end
318
370
 
319
371
  lookup_columns = map_rows_by(rows_columns, 'columnfamily_name')
320
- tables = rows_tables.each_with_object({}) do |row, tables|
372
+ tables = rows_tables.each_with_object({}) do |row, h|
321
373
  table_name = row['columnfamily_name']
322
- tables[table_name] = create_table(row, lookup_columns[table_name])
374
+ # rows_indexes is nil for C* < 3.0.
375
+ h[table_name] = create_table(row, lookup_columns[table_name], nil)
323
376
  end
324
377
 
325
378
  Keyspace.new(keyspace_name,
@@ -328,24 +381,34 @@ module Cassandra
328
381
  tables,
329
382
  types,
330
383
  functions,
331
- aggregates)
384
+ aggregates,
385
+ {})
332
386
  end
333
387
 
334
- def create_table(table_data, rows_columns)
388
+ def create_table(table_data, rows_columns, rows_indexes)
335
389
  keyspace_name = table_data['keyspace_name']
336
390
  table_name = table_data['columnfamily_name']
337
391
  key_validator = @type_parser.parse(table_data['key_validator'])
338
392
  comparator = @type_parser.parse(table_data['comparator'])
339
393
  column_aliases = ::JSON.load(table_data['column_aliases'])
340
394
 
341
- if !comparator.collections.nil?
395
+ if comparator.collections.nil?
396
+ is_compact = true
397
+ if !column_aliases.empty? || rows_columns.empty?
398
+ has_value = true
399
+ clustering_size = comparator.results.size
400
+ else
401
+ has_value = false
402
+ clustering_size = 0
403
+ end
404
+ else
342
405
  size = comparator.results.size
343
406
  if !comparator.collections.empty?
344
407
  is_compact = false
345
408
  has_value = false
346
409
  clustering_size = size - 2
347
410
  elsif column_aliases.size == size - 1 &&
348
- comparator.results.last.first == Cassandra::Types.varchar
411
+ comparator.results.last.first == Cassandra::Types.varchar
349
412
  is_compact = false
350
413
  has_value = false
351
414
  clustering_size = size - 1
@@ -354,33 +417,24 @@ module Cassandra
354
417
  has_value = (!column_aliases.empty? || rows_columns.empty?)
355
418
  clustering_size = size
356
419
  end
357
- else
358
- is_compact = true
359
- if (!column_aliases.empty? || rows_columns.empty?)
360
- has_value = true
361
- clustering_size = comparator.results.size
362
- else
363
- has_value = false
364
- clustering_size = 0
365
- end
366
420
  end
367
421
 
422
+ # Separate out the partition-key, clustering-columns, and other-columns
368
423
  partition_key = []
369
424
  clustering_columns = []
370
425
  clustering_order = []
426
+ other_columns = []
371
427
 
372
428
  compaction_strategy = create_compaction_strategy(table_data)
373
429
  table_options =
374
- create_table_options(table_data, compaction_strategy, is_compact)
375
- table_columns = {}
376
- other_columns = []
430
+ create_table_options(table_data, compaction_strategy, is_compact)
377
431
 
378
432
  key_aliases = ::JSON.load(table_data['key_aliases'])
379
433
 
380
434
  key_validator.results.each_with_index do |(type, order, is_frozen), i|
381
- key_alias = key_aliases.fetch(i) { i.zero? ? "key" : "key#{i + 1}" }
435
+ key_alias = key_aliases.fetch(i) { i.zero? ? 'key' : "key#{i + 1}" }
382
436
 
383
- partition_key[i] = Column.new(key_alias, type, order, nil, false, is_frozen)
437
+ partition_key[i] = Column.new(key_alias, type, order, false, is_frozen)
384
438
  end
385
439
 
386
440
  clustering_size.times do |i|
@@ -388,7 +442,7 @@ module Cassandra
388
442
  type, order, is_frozen = comparator.results.fetch(i)
389
443
 
390
444
  clustering_columns[i] =
391
- Column.new(column_alias, type, order, nil, false, is_frozen)
445
+ Column.new(column_alias, type, order, false, is_frozen)
392
446
  clustering_order[i] = order
393
447
  end
394
448
 
@@ -398,71 +452,86 @@ module Cassandra
398
452
 
399
453
  unless value_alias.empty?
400
454
  type, order, is_frozen =
401
- @type_parser.parse(table_data['default_validator']).results.first
455
+ @type_parser.parse(table_data['default_validator']).results.first
402
456
  other_columns <<
403
- Column.new(value_alias, type, order, nil, false, is_frozen)
457
+ Column.new(value_alias, type, order, false, is_frozen)
404
458
  end
405
459
  end
406
460
 
461
+ index_rows = []
407
462
  rows_columns.each do |row|
408
- other_columns << create_column(row)
409
- end
463
+ column = create_column(row)
464
+ other_columns << column
410
465
 
411
- partition_key.each do |column|
412
- table_columns[column.name] = column
466
+ # In C* 1.2.x, index info is in the column metadata; rows_indexes is nil.
467
+ index_rows << [column, row] unless row['index_type'].nil?
413
468
  end
414
469
 
415
- clustering_columns.each do |column|
416
- table_columns[column.name] = column
470
+ table = Cassandra::Table.new(@schema.keyspace(keyspace_name),
471
+ table_name,
472
+ partition_key,
473
+ clustering_columns,
474
+ other_columns,
475
+ table_options,
476
+ clustering_order,
477
+ table_data['id'])
478
+
479
+ # Create Index objects and add them to the table.
480
+ index_rows.each do |column, row|
481
+ create_index(table, column, row)
417
482
  end
483
+ table
484
+ end
418
485
 
419
- other_columns.each do |column|
420
- table_columns[column.name] = column
486
+ def create_index(table, column, row_column)
487
+ # Most of this logic was taken from the Java driver.
488
+ options = {}
489
+ # For some versions of C*, this field could have a literal string 'null' value.
490
+ if !row_column['index_options'].nil? && row_column['index_options'] != 'null' && !row_column['index_options'].empty?
491
+ options = ::JSON.load(row_column['index_options'])
421
492
  end
422
-
423
- Table.new(keyspace_name,
424
- table_name,
425
- partition_key,
426
- clustering_columns,
427
- table_columns,
428
- table_options,
429
- clustering_order)
493
+ column_name = Util.escape_name(column.name)
494
+ target = if options.key?('index_keys')
495
+ "keys(#{column_name})"
496
+ elsif options.key?('index_keys_and_values')
497
+ "entries(#{column_name})"
498
+ elsif column.frozen? && (column.type == Cassandra::Types::Set ||
499
+ column.type == Cassandra::Types::List ||
500
+ column.type == Cassandra::Types::Map)
501
+ "full(#{column_name})"
502
+ else
503
+ column_name
504
+ end
505
+
506
+ table.add_index(Cassandra::Index.new(table,
507
+ row_column['index_name'],
508
+ row_column['index_type'].downcase.to_sym,
509
+ target,
510
+ options))
430
511
  end
431
512
 
432
513
  def create_column(column_data)
433
514
  name = column_data['column_name']
434
515
  is_static = (column_data['type'] == 'STATIC')
435
516
  type, order, is_frozen =
436
- @type_parser.parse(column_data['validator']).results.first
437
-
438
- if column_data['index_type'].nil?
439
- index = nil
440
- elsif column_data['index_type'].to_s.upcase == 'CUSTOM' ||
441
- !column_data['index_options']
442
- index = Column::Index.new(column_data['index_name'])
443
- else
444
- options = ::JSON.load(column_data['index_options'])
445
- index = Column::Index.new(column_data['index_name'],
446
- options && options['class_name'])
447
- end
448
-
449
- Column.new(name, type, order, index, is_static, is_frozen)
517
+ @type_parser.parse(column_data['validator']).results.first
518
+ Column.new(name, type, order, is_static, is_frozen)
450
519
  end
451
520
 
452
521
  def create_compaction_strategy(table_data)
453
522
  klass = table_data['compaction_strategy_class']
454
523
  klass.slice!('org.apache.cassandra.db.compaction.')
455
524
  options = ::JSON.load(table_data['compaction_strategy_options'])
456
- Table::Compaction.new(klass, options)
525
+ ColumnContainer::Compaction.new(klass, options)
457
526
  end
458
527
 
459
528
  def create_table_options(table_data, compaction_strategy, is_compact)
460
529
  compression_parameters = ::JSON.load(table_data['compression_parameters'])
461
530
  if compression_parameters['sstable_compression']
462
- compression_parameters['sstable_compression'].
463
- slice!(COMPRESSION_PACKAGE_PREFIX)
531
+ compression_parameters['sstable_compression']
532
+ .slice!(COMPRESSION_PACKAGE_PREFIX)
464
533
  end
465
- Table::Options.new(
534
+ Cassandra::ColumnContainer::Options.new(
466
535
  table_data['comment'],
467
536
  table_data['read_repair_chance'],
468
537
  table_data['local_read_repair_chance'],
@@ -479,88 +548,87 @@ module Cassandra
479
548
  nil,
480
549
  compaction_strategy,
481
550
  compression_parameters,
482
- is_compact
551
+ is_compact,
552
+ table_data['crc_check_chance'],
553
+ table_data['extensions']
483
554
  )
484
555
  end
485
556
  end
486
557
 
487
558
  class V2_0_x < V1_2_x
488
559
  SELECT_KEYSPACE =
489
- 'SELECT * FROM system.schema_keyspaces WHERE keyspace_name = ?'.freeze
560
+ 'SELECT * FROM system.schema_keyspaces WHERE keyspace_name = ?'.freeze
490
561
  SELECT_KEYSPACE_TABLES =
491
- 'SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ?'.freeze
562
+ 'SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ?'.freeze
492
563
  SELECT_KEYSPACE_COLUMNS =
493
- 'SELECT * FROM system.schema_columns WHERE keyspace_name = ?'.freeze
564
+ 'SELECT * FROM system.schema_columns WHERE keyspace_name = ?'.freeze
494
565
  SELECT_TABLE =
495
- 'SELECT * ' \
496
- 'FROM system.schema_columnfamilies ' \
497
- 'WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
566
+ 'SELECT * ' \
567
+ 'FROM system.schema_columnfamilies ' \
568
+ 'WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
498
569
  SELECT_TABLE_COLUMNS =
499
- 'SELECT * ' \
500
- 'FROM system.schema_columns ' \
501
- 'WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
570
+ 'SELECT * ' \
571
+ 'FROM system.schema_columns ' \
572
+ 'WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
502
573
 
503
574
  private
504
575
 
505
- def create_table(table_data, rows_columns)
576
+ def create_table(table_data, rows_columns, rows_indexes)
506
577
  keyspace_name = table_data['keyspace_name']
507
578
  table_name = table_data['columnfamily_name']
508
579
  comparator = @type_parser.parse(table_data['comparator'])
509
- table_columns = {}
510
- other_columns = []
511
580
  clustering_size = 0
512
581
 
582
+ # Separate out partition-key, clustering columns, other columns.
513
583
  partition_key = []
514
584
  clustering_columns = []
515
585
  clustering_order = []
586
+ other_columns = []
516
587
 
588
+ index_rows = []
517
589
  rows_columns.each do |row|
518
590
  next if row['column_name'].empty?
519
591
 
520
592
  column = create_column(row)
521
593
  type = row['type'].to_s
522
- index = row['component_index'] || 0
594
+ ind = row['component_index'] || 0
523
595
 
524
596
  case type.upcase
525
597
  when 'PARTITION_KEY'
526
- partition_key[index] = column
598
+ partition_key[ind] = column
527
599
  when 'CLUSTERING_KEY'
528
- clustering_columns[index] = column
529
- clustering_order[index] = column.order
600
+ clustering_columns[ind] = column
601
+ clustering_order[ind] = column.order
530
602
 
531
- if clustering_size.zero? || index == clustering_size
532
- clustering_size = index + 1
533
- end
603
+ clustering_size = ind + 1 if clustering_size.zero? || ind == clustering_size
534
604
  else
535
605
  other_columns << column
536
606
  end
537
- end
538
607
 
539
- partition_key.each do |column|
540
- table_columns[column.name] = column
541
- end
542
-
543
- clustering_columns.each do |column|
544
- table_columns[column.name] = column
545
- end
546
-
547
- other_columns.each do |column|
548
- table_columns[column.name] = column
608
+ # In C* 2.0.x, index info is in the column metadata; rows_indexes is nil.
609
+ index_rows << [column, row] unless row['index_type'].nil?
549
610
  end
550
611
 
551
612
  compaction_strategy = create_compaction_strategy(table_data)
552
613
  is_compact = (clustering_size != comparator.results.size - 1) ||
553
- !comparator.collections
614
+ !comparator.collections
554
615
  table_options =
555
- create_table_options(table_data, compaction_strategy, is_compact)
556
-
557
- Table.new(keyspace_name,
558
- table_name,
559
- partition_key,
560
- clustering_columns,
561
- table_columns,
562
- table_options,
563
- clustering_order)
616
+ create_table_options(table_data, compaction_strategy, is_compact)
617
+
618
+ table = Cassandra::Table.new(@schema.keyspace(keyspace_name),
619
+ table_name,
620
+ partition_key,
621
+ clustering_columns,
622
+ other_columns,
623
+ table_options,
624
+ clustering_order,
625
+ table_data['id'])
626
+
627
+ # Create Index objects and add them to the table.
628
+ index_rows.each do |column, row|
629
+ create_index(table, column, row)
630
+ end
631
+ table
564
632
  end
565
633
 
566
634
  def select_keyspace(connection, keyspace_name)
@@ -596,10 +664,10 @@ module Cassandra
596
664
  def create_table_options(table_data, compaction_strategy, is_compact)
597
665
  compression_parameters = ::JSON.load(table_data['compression_parameters'])
598
666
  if compression_parameters['sstable_compression']
599
- compression_parameters['sstable_compression'].
600
- slice!(COMPRESSION_PACKAGE_PREFIX)
667
+ compression_parameters['sstable_compression']
668
+ .slice!(COMPRESSION_PACKAGE_PREFIX)
601
669
  end
602
- Table::Options.new(
670
+ Cassandra::ColumnContainer::Options.new(
603
671
  table_data['comment'],
604
672
  table_data['read_repair_chance'],
605
673
  table_data['local_read_repair_chance'],
@@ -616,7 +684,9 @@ module Cassandra
616
684
  nil,
617
685
  compaction_strategy,
618
686
  compression_parameters,
619
- is_compact
687
+ is_compact,
688
+ table_data['crc_check_chance'],
689
+ table_data['extensions']
620
690
  )
621
691
  end
622
692
  end
@@ -624,11 +694,11 @@ module Cassandra
624
694
  class V2_1_x < V2_0_x
625
695
  SELECT_TYPES = 'SELECT * FROM system.schema_usertypes'.freeze
626
696
  SELECT_KEYSPACE_TYPES =
627
- 'SELECT * FROM system.schema_usertypes WHERE keyspace_name = ?'.freeze
697
+ 'SELECT * FROM system.schema_usertypes WHERE keyspace_name = ?'.freeze
628
698
  SELECT_TYPE =
629
- 'SELECT * ' \
630
- 'FROM system.schema_usertypes ' \
631
- 'WHERE keyspace_name = ? AND type_name = ?'.freeze
699
+ 'SELECT * ' \
700
+ 'FROM system.schema_usertypes ' \
701
+ 'WHERE keyspace_name = ? AND type_name = ?'.freeze
632
702
 
633
703
  private
634
704
 
@@ -668,10 +738,10 @@ module Cassandra
668
738
  def create_table_options(table_data, compaction_strategy, is_compact)
669
739
  compression_parameters = ::JSON.load(table_data['compression_parameters'])
670
740
  if compression_parameters['sstable_compression']
671
- compression_parameters['sstable_compression'].
672
- slice!(COMPRESSION_PACKAGE_PREFIX)
741
+ compression_parameters['sstable_compression']
742
+ .slice!(COMPRESSION_PACKAGE_PREFIX)
673
743
  end
674
- Table::Options.new(
744
+ Cassandra::ColumnContainer::Options.new(
675
745
  table_data['comment'],
676
746
  table_data['read_repair_chance'],
677
747
  table_data['local_read_repair_chance'],
@@ -688,7 +758,9 @@ module Cassandra
688
758
  table_data['max_index_interval'],
689
759
  compaction_strategy,
690
760
  compression_parameters,
691
- is_compact
761
+ is_compact,
762
+ table_data['crc_check_chance'],
763
+ table_data['extensions']
692
764
  )
693
765
  end
694
766
  end
@@ -697,19 +769,19 @@ module Cassandra
697
769
  SELECT_FUNCTIONS = 'SELECT * FROM system.schema_functions'.freeze
698
770
  SELECT_AGGREGATES = 'SELECT * FROM system.schema_aggregates'.freeze
699
771
  SELECT_KEYSPACE_FUNCTIONS =
700
- 'SELECT * FROM system.schema_functions WHERE keyspace_name = ?'.freeze
772
+ 'SELECT * FROM system.schema_functions WHERE keyspace_name = ?'.freeze
701
773
  SELECT_KEYSPACE_AGGREGATES =
702
- 'SELECT * FROM system.schema_aggregates WHERE keyspace_name = ?'.freeze
774
+ 'SELECT * FROM system.schema_aggregates WHERE keyspace_name = ?'.freeze
703
775
  SELECT_FUNCTION =
704
- 'SELECT * ' \
705
- 'FROM system.schema_functions ' \
706
- 'WHERE keyspace_name = ? AND function_name = ? ' \
707
- 'AND argument_types = ?'.freeze
776
+ 'SELECT * ' \
777
+ 'FROM system.schema_functions ' \
778
+ 'WHERE keyspace_name = ? AND function_name = ? ' \
779
+ 'AND argument_types = ?'.freeze
708
780
  SELECT_AGGREGATE =
709
- 'SELECT * ' \
710
- 'FROM system.schema_aggregates ' \
711
- 'WHERE keyspace_name = ? AND aggregate_name = ? ' \
712
- 'AND argument_types = ?'.freeze
781
+ 'SELECT * ' \
782
+ 'FROM system.schema_aggregates ' \
783
+ 'WHERE keyspace_name = ? AND aggregate_name = ? ' \
784
+ 'AND argument_types = ?'.freeze
713
785
 
714
786
  # parse an array of string argument types and return an array of
715
787
  # [Cassandra::Type]s.
@@ -730,41 +802,41 @@ module Cassandra
730
802
  function_name = function_data['function_name']
731
803
  function_lang = function_data['language']
732
804
  function_type =
733
- @type_parser.parse(function_data['return_type']).results.first.first
805
+ @type_parser.parse(function_data['return_type']).results.first.first
734
806
  function_body = function_data['body']
735
807
  called_on_null = function_data['called_on_null_input']
736
808
 
737
809
  arguments = []
738
810
 
739
- Array(function_data['argument_names']).
740
- zip(Array(function_data['argument_types'])) do |argument_name, fqcn|
811
+ Array(function_data['argument_names'])
812
+ .zip(Array(function_data['argument_types'])) do |argument_name, fqcn|
741
813
  argument_type = @type_parser.parse(fqcn).results.first.first
742
814
  arguments << Argument.new(argument_name, argument_type)
743
815
  end
744
816
 
745
- Function.new(keyspace_name,
746
- function_name,
747
- function_lang,
748
- function_type,
749
- arguments,
750
- function_body,
751
- called_on_null)
817
+ Cassandra::Function.new(keyspace_name,
818
+ function_name,
819
+ function_lang,
820
+ function_type,
821
+ arguments,
822
+ function_body,
823
+ called_on_null)
752
824
  end
753
825
 
754
826
  def create_aggregate(aggregate_data, functions)
755
827
  keyspace_name = aggregate_data['keyspace_name']
756
828
  aggregate_name = aggregate_data['aggregate_name']
757
829
  aggregate_type =
758
- @type_parser.parse(aggregate_data['return_type']).results.first.first
830
+ @type_parser.parse(aggregate_data['return_type']).results.first.first
759
831
  argument_types = aggregate_data['argument_types'].map do |fqcn|
760
832
  @type_parser.parse(fqcn).results.first.first
761
833
  end.freeze
762
834
  state_type =
763
- @type_parser.parse(aggregate_data['state_type']).results.first.first
835
+ @type_parser.parse(aggregate_data['state_type']).results.first.first
764
836
  initial_state = Util.encode_object(
765
- Protocol::Coder.read_value_v4(
766
- Protocol::CqlByteBuffer.new.append_bytes(aggregate_data['initcond']),
767
- state_type))
837
+ Protocol::Coder.read_value_v4(
838
+ Protocol::CqlByteBuffer.new.append_bytes(aggregate_data['initcond']),
839
+ state_type))
768
840
 
769
841
  # The state-function takes arguments: first the stype, then the args of the aggregate.
770
842
  state_function = functions.get(aggregate_data['state_func'],
@@ -818,53 +890,66 @@ module Cassandra
818
890
  end
819
891
 
820
892
  class V3_0_x < V2_2_x
821
- SELECT_KEYSPACES = "SELECT * FROM system_schema.keyspaces".freeze
822
- SELECT_TABLES = "SELECT * FROM system_schema.tables".freeze
823
- SELECT_COLUMNS = "SELECT * FROM system_schema.columns".freeze
824
- SELECT_TYPES = "SELECT * FROM system_schema.types".freeze
825
- SELECT_FUNCTIONS = "SELECT * FROM system_schema.functions".freeze
826
- SELECT_AGGREGATES = "SELECT * FROM system_schema.aggregates".freeze
827
- SELECT_INDEXES = "SELECT * FROM system_schema.indexes".freeze
828
- SELECT_VIEWS = "SELECT * FROM system_schema.materialized_views".freeze
893
+ SELECT_KEYSPACES = 'SELECT * FROM system_schema.keyspaces'.freeze
894
+ SELECT_TABLES = 'SELECT * FROM system_schema.tables'.freeze
895
+ SELECT_COLUMNS = 'SELECT * FROM system_schema.columns'.freeze
896
+ SELECT_TYPES = 'SELECT * FROM system_schema.types'.freeze
897
+ SELECT_FUNCTIONS = 'SELECT * FROM system_schema.functions'.freeze
898
+ SELECT_AGGREGATES = 'SELECT * FROM system_schema.aggregates'.freeze
899
+ SELECT_INDEXES = 'SELECT * FROM system_schema.indexes'.freeze
900
+ SELECT_VIEWS = 'SELECT * FROM system_schema.views'.freeze
829
901
 
830
902
  SELECT_KEYSPACE =
831
- 'SELECT * FROM system_schema.keyspaces WHERE keyspace_name = ?'.freeze
903
+ 'SELECT * FROM system_schema.keyspaces WHERE keyspace_name = ?'.freeze
832
904
  SELECT_KEYSPACE_TABLES =
833
- 'SELECT * FROM system_schema.tables WHERE keyspace_name = ?'.freeze
905
+ 'SELECT * FROM system_schema.tables WHERE keyspace_name = ?'.freeze
906
+ SELECT_KEYSPACE_INDEXES =
907
+ 'SELECT * FROM system_schema.indexes WHERE keyspace_name = ?'.freeze
834
908
  SELECT_KEYSPACE_COLUMNS =
835
- 'SELECT * FROM system_schema.columns WHERE keyspace_name = ?'.freeze
909
+ 'SELECT * FROM system_schema.columns WHERE keyspace_name = ?'.freeze
910
+ SELECT_KEYSPACE_VIEWS =
911
+ 'SELECT * FROM system_schema.views WHERE keyspace_name = ?'.freeze
836
912
  SELECT_KEYSPACE_TYPES =
837
- "SELECT * FROM system_schema.types WHERE keyspace_name = ?".freeze
913
+ 'SELECT * FROM system_schema.types WHERE keyspace_name = ?'.freeze
838
914
  SELECT_KEYSPACE_FUNCTIONS =
839
- 'SELECT * FROM system_schema.functions WHERE keyspace_name = ?'.freeze
915
+ 'SELECT * FROM system_schema.functions WHERE keyspace_name = ?'.freeze
840
916
  SELECT_KEYSPACE_AGGREGATES =
841
- 'SELECT * FROM system_schema.aggregates WHERE keyspace_name = ?'.freeze
917
+ 'SELECT * FROM system_schema.aggregates WHERE keyspace_name = ?'.freeze
842
918
 
843
919
  SELECT_TABLE =
844
- 'SELECT * ' \
845
- 'FROM system_schema.tables ' \
846
- 'WHERE keyspace_name = ? AND table_name = ?'.freeze
920
+ 'SELECT * ' \
921
+ 'FROM system_schema.tables ' \
922
+ 'WHERE keyspace_name = ? AND table_name = ?'.freeze
847
923
  SELECT_TABLE_COLUMNS =
848
- 'SELECT * ' \
849
- 'FROM system_schema.columns ' \
850
- 'WHERE keyspace_name = ? AND table_name = ?'.freeze
924
+ 'SELECT * ' \
925
+ 'FROM system_schema.columns ' \
926
+ 'WHERE keyspace_name = ? AND table_name = ?'.freeze
927
+ SELECT_TABLE_INDEXES =
928
+ 'SELECT * ' \
929
+ 'FROM system_schema.indexes ' \
930
+ 'WHERE keyspace_name = ? AND table_name = ?'.freeze
931
+
932
+ SELECT_VIEW =
933
+ 'SELECT * ' \
934
+ 'FROM system_schema.views ' \
935
+ 'WHERE keyspace_name = ? AND view_name = ?'.freeze
851
936
 
852
937
  SELECT_TYPE =
853
- 'SELECT * ' \
854
- 'FROM system_schema.types ' \
855
- 'WHERE keyspace_name = ? AND type_name = ?'.freeze
938
+ 'SELECT * ' \
939
+ 'FROM system_schema.types ' \
940
+ 'WHERE keyspace_name = ? AND type_name = ?'.freeze
856
941
 
857
942
  SELECT_FUNCTION =
858
- 'SELECT * ' \
859
- 'FROM system_schema.functions ' \
860
- 'WHERE keyspace_name = ? AND function_name = ? ' \
861
- 'AND argument_types = ?'.freeze
943
+ 'SELECT * ' \
944
+ 'FROM system_schema.functions ' \
945
+ 'WHERE keyspace_name = ? AND function_name = ? ' \
946
+ 'AND argument_types = ?'.freeze
862
947
 
863
948
  SELECT_AGGREGATE =
864
- 'SELECT * ' \
865
- 'FROM system_schema.aggregates ' \
866
- 'WHERE keyspace_name = ? AND aggregate_name = ? ' \
867
- 'AND argument_types = ?'.freeze
949
+ 'SELECT * ' \
950
+ 'FROM system_schema.aggregates ' \
951
+ 'WHERE keyspace_name = ? AND aggregate_name = ? ' \
952
+ 'AND argument_types = ?'.freeze
868
953
 
869
954
  # parse an array of string argument types and return an array of
870
955
  # [Cassandra::Type]s.
@@ -889,6 +974,14 @@ module Cassandra
889
974
  send_select_request(connection, SELECT_TABLES)
890
975
  end
891
976
 
977
+ def select_indexes(connection)
978
+ send_select_request(connection, SELECT_INDEXES)
979
+ end
980
+
981
+ def select_materialized_views(connection)
982
+ send_select_request(connection, SELECT_VIEWS)
983
+ end
984
+
892
985
  def select_columns(connection)
893
986
  send_select_request(connection, SELECT_COLUMNS)
894
987
  end
@@ -923,6 +1016,18 @@ module Cassandra
923
1016
  send_select_request(connection, SELECT_KEYSPACE_COLUMNS, params, hints)
924
1017
  end
925
1018
 
1019
+ def select_keyspace_indexes(connection, keyspace_name)
1020
+ params = [keyspace_name]
1021
+ hints = [Types.varchar]
1022
+ send_select_request(connection, SELECT_KEYSPACE_INDEXES, params, hints)
1023
+ end
1024
+
1025
+ def select_keyspace_materialized_views(connection, keyspace_name)
1026
+ params = [keyspace_name]
1027
+ hints = [Types.varchar]
1028
+ send_select_request(connection, SELECT_KEYSPACE_VIEWS, params, hints)
1029
+ end
1030
+
926
1031
  def select_keyspace_types(connection, keyspace_name)
927
1032
  params = [keyspace_name]
928
1033
  hints = [Types.varchar]
@@ -948,11 +1053,26 @@ module Cassandra
948
1053
  end
949
1054
 
950
1055
  def select_table_columns(connection, keyspace_name, table_name)
1056
+ # This is identical to the 2.0 impl, but the SELECT_TABLE_COLUMNS query
1057
+ # is different between the two, so we need two impls. :(
1058
+ # Also, this method works fine for finding view columns as well.
951
1059
  params = [keyspace_name, table_name]
952
1060
  hints = [Types.varchar, Types.varchar]
953
1061
  send_select_request(connection, SELECT_TABLE_COLUMNS, params, hints)
954
1062
  end
955
1063
 
1064
+ def select_table_indexes(connection, keyspace_name, table_name)
1065
+ params = [keyspace_name, table_name]
1066
+ hints = [Types.varchar, Types.varchar]
1067
+ send_select_request(connection, SELECT_TABLE_INDEXES, params, hints)
1068
+ end
1069
+
1070
+ def select_materialized_view(connection, keyspace_name, view_name)
1071
+ params = [keyspace_name, view_name]
1072
+ hints = [Types.varchar, Types.varchar]
1073
+ send_select_request(connection, SELECT_VIEW, params, hints)
1074
+ end
1075
+
956
1076
  def select_type(connection, keyspace_name, type_name)
957
1077
  params = [keyspace_name, type_name]
958
1078
  hints = [Types.varchar, Types.varchar]
@@ -982,19 +1102,19 @@ module Cassandra
982
1102
 
983
1103
  arguments = []
984
1104
 
985
- function_data['argument_names'].
986
- zip(function_data['argument_types']) do |argument_name, argument_type|
1105
+ function_data['argument_names']
1106
+ .zip(function_data['argument_types']) do |argument_name, argument_type|
987
1107
  arguments << Argument.new(argument_name,
988
1108
  @type_parser.parse(argument_type, types).first)
989
1109
  end
990
1110
 
991
- Function.new(keyspace_name,
992
- function_name,
993
- function_lang,
994
- function_type,
995
- arguments,
996
- function_body,
997
- called_on_null)
1111
+ Cassandra::Function.new(keyspace_name,
1112
+ function_name,
1113
+ function_lang,
1114
+ function_type,
1115
+ arguments,
1116
+ function_body,
1117
+ called_on_null)
998
1118
  end
999
1119
 
1000
1120
  def create_aggregate(aggregate_data, functions, types = nil)
@@ -1002,7 +1122,7 @@ module Cassandra
1002
1122
  aggregate_name = aggregate_data['aggregate_name']
1003
1123
  types ||= @schema.keyspace(keyspace_name).send(:raw_types)
1004
1124
  aggregate_type =
1005
- @type_parser.parse(aggregate_data['return_type'], types).first
1125
+ @type_parser.parse(aggregate_data['return_type'], types).first
1006
1126
  argument_types = aggregate_data['argument_types'].map do |argument_type|
1007
1127
  @type_parser.parse(argument_type, types).first
1008
1128
  end.freeze
@@ -1057,16 +1177,14 @@ module Cassandra
1057
1177
 
1058
1178
  break if skipped_rows.empty?
1059
1179
 
1060
- if rows_size == skipped_rows.size
1061
- raise "Unable to resolve circular references among UDTs when parsing"
1062
- else
1063
- rows_types, skipped_rows = skipped_rows, rows_types
1064
- end
1180
+ raise 'Unable to resolve circular references among UDTs when parsing' if rows_size == skipped_rows.size
1181
+
1182
+ rows_types, skipped_rows = skipped_rows, rows_types
1065
1183
  end
1066
1184
  end
1067
1185
 
1068
- def create_keyspace(keyspace_data, rows_tables, rows_columns,
1069
- rows_types, rows_functions, rows_aggregates)
1186
+ def create_keyspace(keyspace_data, rows_tables, rows_columns, rows_types,
1187
+ rows_functions, rows_aggregates, rows_views, rows_indexes)
1070
1188
  keyspace_name = keyspace_data['keyspace_name']
1071
1189
  replication = create_replication(keyspace_data)
1072
1190
 
@@ -1084,10 +1202,25 @@ module Cassandra
1084
1202
  aggregates.add_or_update(create_aggregate(row, functions, types))
1085
1203
  end
1086
1204
 
1205
+ # lookup_columns is a hash of <table-name, rows_columns for that table>.
1206
+ # However, views are analogous to tables in this context, so we get
1207
+ # view columns organized by view-name also.
1208
+
1087
1209
  lookup_columns = map_rows_by(rows_columns, 'table_name')
1088
- tables = rows_tables.each_with_object({}) do |row, tables|
1210
+ lookup_indexes = map_rows_by(rows_indexes, 'table_name')
1211
+ tables = rows_tables.each_with_object({}) do |row, h|
1089
1212
  table_name = row['table_name']
1090
- tables[table_name] = create_table(row, lookup_columns[table_name], types)
1213
+ h[table_name] = create_table(row, lookup_columns[table_name],
1214
+ lookup_indexes[table_name], types)
1215
+ end
1216
+
1217
+ views = rows_views.each_with_object({}) do |row, h|
1218
+ view_name = row['view_name']
1219
+ base_table = tables[row['base_table_name']]
1220
+ h[view_name] = create_materialized_view(row,
1221
+ lookup_columns[view_name],
1222
+ base_table,
1223
+ types)
1091
1224
  end
1092
1225
 
1093
1226
  Keyspace.new(keyspace_name,
@@ -1096,7 +1229,8 @@ module Cassandra
1096
1229
  tables,
1097
1230
  types,
1098
1231
  functions,
1099
- aggregates)
1232
+ aggregates,
1233
+ views)
1100
1234
  end
1101
1235
 
1102
1236
  def create_replication(keyspace_data)
@@ -1110,16 +1244,14 @@ module Cassandra
1110
1244
  options = table_data['compaction']
1111
1245
  klass = options.delete('class')
1112
1246
  klass.slice!('org.apache.cassandra.db.compaction.')
1113
- Table::Compaction.new(klass, options)
1247
+ ColumnContainer::Compaction.new(klass, options)
1114
1248
  end
1115
1249
 
1116
1250
  def create_table_options(table_data, compaction_strategy, is_compact)
1117
1251
  compression = table_data['compression']
1118
- if compression['class']
1119
- compression['class'].slice!(COMPRESSION_PACKAGE_PREFIX)
1120
- end
1252
+ compression['class'].slice!(COMPRESSION_PACKAGE_PREFIX) if compression['class']
1121
1253
 
1122
- Table::Options.new(
1254
+ Cassandra::ColumnContainer::Options.new(
1123
1255
  table_data['comment'],
1124
1256
  table_data['read_repair_chance'],
1125
1257
  table_data['dclocal_read_repair_chance'],
@@ -1136,82 +1268,146 @@ module Cassandra
1136
1268
  table_data['max_index_interval'],
1137
1269
  compaction_strategy,
1138
1270
  compression,
1139
- is_compact
1271
+ is_compact,
1272
+ table_data['crc_check_chance'],
1273
+ table_data['extensions']
1140
1274
  )
1141
1275
  end
1142
1276
 
1143
1277
  def create_column(column_data, types)
1144
1278
  name = column_data['column_name']
1145
- is_static = (column_data['kind'].to_s.upcase == 'STATIC')
1279
+ is_static = column_data['kind'].to_s.casecmp('STATIC').zero?
1146
1280
  order = column_data['clustering_order'] == 'desc' ? :desc : :asc
1147
- type, is_frozen = @type_parser.parse(column_data['type'], types)
1281
+ if column_data['type'][0] == "'"
1282
+ # This is a custom column type.
1283
+ type = Types.custom(column_data['type'].slice(1, column_data['type'].length - 2))
1284
+ is_frozen = false
1285
+ else
1286
+ type, is_frozen = @type_parser.parse(column_data['type'], types)
1287
+ end
1148
1288
 
1149
- Column.new(name, type, order, nil, is_static, is_frozen)
1289
+ Column.new(name, type, order, is_static, is_frozen)
1150
1290
  end
1151
1291
 
1152
- def create_table(table_data, rows_columns, types = nil)
1292
+ def create_table(table_data, rows_columns, rows_indexes, types = nil)
1153
1293
  keyspace_name = table_data['keyspace_name']
1154
1294
  table_name = table_data['table_name']
1155
1295
  table_flags = table_data['flags']
1156
- table_columns = {}
1157
- other_columns = []
1158
- clustering_size = 0
1159
1296
 
1160
1297
  is_dense = table_flags.include?('dense')
1161
1298
  is_super = table_flags.include?('super')
1162
1299
  is_compound = table_flags.include?('compound')
1163
1300
  is_compact = is_super || is_dense || !is_compound
1301
+ is_static_compact = !is_super && !is_dense && !is_compound
1164
1302
 
1303
+ # Separate out partition-key, clustering columns, other columns.
1165
1304
  partition_key = []
1166
1305
  clustering_columns = []
1167
1306
  clustering_order = []
1168
- types ||= @schema.keyspace(keyspace_name).send(:raw_types)
1307
+ other_columns = []
1308
+ types ||= @schema.keyspace(keyspace_name).send(:raw_types)
1169
1309
 
1170
1310
  rows_columns.each do |row|
1171
1311
  next if row['column_name'].empty?
1172
1312
 
1173
- column = create_column(row, types)
1174
1313
  kind = row['kind'].to_s
1175
1314
  index = row['position'] || 0
1176
1315
 
1316
+ if is_static_compact
1317
+ if kind.casecmp('CLUSTERING').zero? || kind.casecmp('REGULAR').zero?
1318
+ # Skip clustering columns in static-compact tables; they are internal to C*.
1319
+ # Oddly so are regular columns.
1320
+ next
1321
+ end
1322
+ if kind.casecmp('STATIC').zero?
1323
+ # Coerce static type to regular.
1324
+ kind = 'REGULAR'
1325
+ row['kind'] = 'regular'
1326
+ end
1327
+ end
1328
+
1329
+ column = create_column(row, types)
1177
1330
  case kind.upcase
1178
1331
  when 'PARTITION_KEY'
1179
1332
  partition_key[index] = column
1180
1333
  when 'CLUSTERING'
1181
1334
  clustering_columns[index] = column
1182
1335
  clustering_order[index] = column.order
1183
-
1184
- if clustering_size.zero? || index == clustering_size
1185
- clustering_size = index + 1
1186
- end
1187
1336
  else
1188
1337
  other_columns << column
1189
1338
  end
1190
1339
  end
1191
1340
 
1192
- partition_key.each do |column|
1193
- table_columns[column.name] = column
1341
+ # Default the crc_check_chance to 1.0 (Java driver does this, so we
1342
+ # should, too).
1343
+ table_data['crc_check_chance'] ||= 1.0
1344
+ compaction_strategy = create_compaction_strategy(table_data)
1345
+ table_options =
1346
+ create_table_options(table_data, compaction_strategy, is_compact)
1347
+
1348
+ table = Cassandra::Table.new(@schema.keyspace(keyspace_name),
1349
+ table_name,
1350
+ partition_key,
1351
+ clustering_columns,
1352
+ other_columns,
1353
+ table_options,
1354
+ clustering_order,
1355
+ table_data['id'])
1356
+ rows_indexes.each do |row|
1357
+ create_index(table, row)
1194
1358
  end
1359
+ table
1360
+ end
1195
1361
 
1196
- clustering_columns.each do |column|
1197
- table_columns[column.name] = column
1198
- end
1362
+ def create_index(table, row_index)
1363
+ options = row_index['options']
1364
+ table.add_index(Cassandra::Index.new(table, row_index['index_name'],
1365
+ row_index['kind'].downcase.to_sym,
1366
+ options['target'], options))
1367
+ end
1368
+
1369
+ def create_materialized_view(view_data, rows_columns, base_table, types = nil)
1370
+ keyspace_name = view_data['keyspace_name']
1371
+ view_name = view_data['view_name']
1372
+ include_all_columns = view_data['include_all_columns']
1373
+ where_clause = view_data['where_clause']
1199
1374
 
1200
- other_columns.each do |column|
1201
- table_columns[column.name] = column
1375
+ # Separate out partition key, clustering columns, other columns
1376
+ partition_key = []
1377
+ clustering_columns = []
1378
+ other_columns = []
1379
+ types ||= @schema.keyspace(keyspace_name).send(:raw_types)
1380
+
1381
+ rows_columns.each do |row|
1382
+ next if row['column_name'].empty?
1383
+
1384
+ column = create_column(row, types)
1385
+ kind = row['kind'].to_s
1386
+ index = row['position'] || 0
1387
+
1388
+ case kind.upcase
1389
+ when 'PARTITION_KEY'
1390
+ partition_key[index] = column
1391
+ when 'CLUSTERING'
1392
+ clustering_columns[index] = column
1393
+ else
1394
+ other_columns << column
1395
+ end
1202
1396
  end
1203
1397
 
1204
- compaction_strategy = create_compaction_strategy(table_data)
1205
- table_options =
1206
- create_table_options(table_data, compaction_strategy, is_compact)
1398
+ compaction_strategy = create_compaction_strategy(view_data)
1399
+ view_options = create_table_options(view_data, compaction_strategy, false)
1207
1400
 
1208
- Table.new(keyspace_name,
1209
- table_name,
1210
- partition_key,
1211
- clustering_columns,
1212
- table_columns,
1213
- table_options,
1214
- clustering_order)
1401
+ MaterializedView.new(@schema.keyspace(keyspace_name),
1402
+ view_name,
1403
+ partition_key,
1404
+ clustering_columns,
1405
+ other_columns,
1406
+ view_options,
1407
+ include_all_columns,
1408
+ where_clause,
1409
+ base_table,
1410
+ view_data['id'])
1215
1411
  end
1216
1412
  end
1217
1413
 
@@ -1263,6 +1459,13 @@ module Cassandra
1263
1459
  return Ione::Future.failed(e)
1264
1460
  end
1265
1461
 
1462
+ def fetch_materialized_view(connection, keyspace_name, view_name)
1463
+ find_fetcher(connection)
1464
+ .fetch_materialized_view(connection, keyspace_name, view_name)
1465
+ rescue => e
1466
+ return Ione::Future.failed(e)
1467
+ end
1468
+
1266
1469
  def fetch_type(connection, keyspace_name, type_name)
1267
1470
  find_fetcher(connection)
1268
1471
  .fetch_type(connection, keyspace_name, type_name)
@@ -1304,21 +1507,21 @@ module Cassandra
1304
1507
  unless host
1305
1508
  ips = @registry.hosts.map(&:ip)
1306
1509
  raise Errors::ClientError,
1307
- "unable to find release version for current host, " \
1510
+ 'unable to find release version for current host, ' \
1308
1511
  "connected to #{connection.host}, but cluster contains " \
1309
1512
  "#{ips}."
1310
1513
  end
1311
1514
 
1312
1515
  version = host.release_version
1313
1516
  unless version
1314
- raise Errors::ClientError, "unable to determine release " \
1517
+ raise Errors::ClientError, 'unable to determine release ' \
1315
1518
  "version for host: #{host.inspect}"
1316
1519
  end
1317
1520
 
1318
1521
  @fetchers[version] ||= begin
1319
1522
  current = @versions.find {|v| v.matches?(version)}
1320
1523
  unless current
1321
- raise Errors::ClientError, "unsupported release version " \
1524
+ raise Errors::ClientError, 'unsupported release version ' \
1322
1525
  "#{version.inspect}."
1323
1526
  end
1324
1527
  current.fetcher