cassandra-driver 3.0.0.rc.1-java → 3.0.0.rc.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 (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