cassandra-driver 2.1.7-java → 3.0.0.beta.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -53
  3. data/lib/cassandra.rb +22 -3
  4. data/lib/cassandra/aggregate.rb +109 -0
  5. data/lib/cassandra/argument.rb +51 -0
  6. data/lib/cassandra/auth/providers/password.rb +7 -4
  7. data/lib/cassandra/cluster.rb +14 -3
  8. data/lib/cassandra/cluster/client.rb +56 -34
  9. data/lib/cassandra/cluster/connector.rb +6 -6
  10. data/lib/cassandra/cluster/control_connection.rb +204 -251
  11. data/lib/cassandra/cluster/metadata.rb +2 -0
  12. data/lib/cassandra/cluster/schema.rb +131 -209
  13. data/lib/cassandra/cluster/schema/cql_type_parser.rb +104 -0
  14. data/lib/cassandra/cluster/schema/fetchers.rb +1174 -0
  15. data/lib/cassandra/cluster/schema/{type_parser.rb → fqcn_type_parser.rb} +7 -3
  16. data/lib/cassandra/column.rb +2 -2
  17. data/lib/cassandra/driver.rb +27 -9
  18. data/lib/cassandra/errors.rb +179 -25
  19. data/lib/cassandra/execution/info.rb +8 -1
  20. data/lib/cassandra/execution/options.rb +34 -0
  21. data/lib/cassandra/execution/trace.rb +42 -10
  22. data/lib/cassandra/function.rb +150 -0
  23. data/lib/cassandra/future.rb +66 -35
  24. data/lib/cassandra/host.rb +7 -4
  25. data/lib/cassandra/keyspace.rb +112 -13
  26. data/lib/cassandra/load_balancing.rb +1 -1
  27. data/lib/cassandra/protocol.rb +9 -3
  28. data/lib/cassandra/protocol/coder.rb +434 -155
  29. data/lib/cassandra/protocol/cql_byte_buffer.rb +43 -0
  30. data/lib/cassandra/protocol/cql_protocol_handler.rb +4 -1
  31. data/lib/cassandra/protocol/request.rb +4 -0
  32. data/lib/cassandra/protocol/requests/auth_response_request.rb +5 -1
  33. data/lib/cassandra/protocol/requests/batch_request.rb +7 -2
  34. data/lib/cassandra/protocol/requests/credentials_request.rb +5 -1
  35. data/lib/cassandra/protocol/requests/execute_request.rb +16 -10
  36. data/lib/cassandra/protocol/requests/prepare_request.rb +12 -3
  37. data/lib/cassandra/protocol/requests/query_request.rb +20 -11
  38. data/lib/cassandra/protocol/responses/already_exists_error_response.rb +4 -4
  39. data/lib/cassandra/protocol/responses/error_response.rb +14 -14
  40. data/lib/cassandra/protocol/responses/function_failure_error_response.rb +41 -0
  41. data/lib/cassandra/protocol/responses/prepared_result_response.rb +12 -9
  42. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +5 -3
  43. data/lib/cassandra/protocol/responses/read_failure_error_response.rb +43 -0
  44. data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +4 -4
  45. data/lib/cassandra/protocol/responses/ready_response.rb +5 -1
  46. data/lib/cassandra/protocol/responses/result_response.rb +3 -3
  47. data/lib/cassandra/protocol/responses/rows_result_response.rb +2 -2
  48. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +25 -24
  49. data/lib/cassandra/protocol/responses/schema_change_result_response.rb +20 -23
  50. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +2 -2
  51. data/lib/cassandra/protocol/responses/unavailable_error_response.rb +4 -4
  52. data/lib/cassandra/protocol/responses/unprepared_error_response.rb +4 -4
  53. data/lib/cassandra/protocol/responses/write_failure_error_response.rb +45 -0
  54. data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +4 -4
  55. data/lib/cassandra/protocol/v1.rb +38 -13
  56. data/lib/cassandra/protocol/v3.rb +34 -29
  57. data/lib/cassandra/protocol/v4.rb +334 -0
  58. data/lib/cassandra/result.rb +10 -9
  59. data/lib/cassandra/retry.rb +17 -3
  60. data/lib/cassandra/retry/policies/default.rb +9 -3
  61. data/lib/cassandra/session.rb +15 -7
  62. data/lib/cassandra/statement.rb +5 -0
  63. data/lib/cassandra/statements/batch.rb +36 -12
  64. data/lib/cassandra/statements/bound.rb +2 -1
  65. data/lib/cassandra/statements/prepared.rb +106 -35
  66. data/lib/cassandra/statements/simple.rb +4 -2
  67. data/lib/cassandra/table.rb +70 -105
  68. data/lib/cassandra/time.rb +98 -0
  69. data/lib/cassandra/time_uuid.rb +1 -1
  70. data/lib/cassandra/tuple.rb +7 -0
  71. data/lib/cassandra/types.rb +472 -272
  72. data/lib/cassandra/udt.rb +10 -0
  73. data/lib/cassandra/util.rb +32 -1
  74. data/lib/cassandra/uuid.rb +6 -1
  75. data/lib/cassandra/uuid/generator.rb +7 -7
  76. data/lib/cassandra/version.rb +1 -1
  77. data/lib/cassandra_murmur3.jar +0 -0
  78. data/lib/datastax/cassandra.rb +5 -2
  79. metadata +27 -17
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2015 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ class Cluster
21
+ class Schema
22
+ class CQLTypeParser
23
+ IncompleteTypeError = ::Class.new(::StandardError)
24
+
25
+ # @private
26
+ Node = Struct.new(:parent, :name, :children)
27
+
28
+ def parse(string, types)
29
+ frozen = false
30
+ node = parse_node(string)
31
+
32
+ if node.name == 'frozen'
33
+ frozen = true
34
+ node = node.children.first
35
+ end
36
+
37
+ [lookup_type(node, types), frozen]
38
+ end
39
+
40
+ private
41
+
42
+ def lookup_type(node, types)
43
+ if node.name == 'frozen'
44
+ return lookup_type(node.children.first, types)
45
+ end
46
+
47
+ case node.name
48
+ when 'text' then Cassandra::Types.text
49
+ when 'blob' then Cassandra::Types.blob
50
+ when 'ascii' then Cassandra::Types.ascii
51
+ when 'bigint' then Cassandra::Types.bigint
52
+ when 'counter' then Cassandra::Types.counter
53
+ when 'int' then Cassandra::Types.int
54
+ when 'varint' then Cassandra::Types.varint
55
+ when 'boolean' then Cassandra::Types.boolean
56
+ when 'decimal' then Cassandra::Types.decimal
57
+ when 'double' then Cassandra::Types.double
58
+ when 'float' then Cassandra::Types.float
59
+ when 'inet' then Cassandra::Types.inet
60
+ when 'timestamp' then Cassandra::Types.timestamp
61
+ when 'uuid' then Cassandra::Types.uuid
62
+ when 'timeuuid' then Cassandra::Types.timeuuid
63
+ when 'date' then Cassandra::Types.date
64
+ when 'smallint' then Cassandra::Types.smallint
65
+ when 'time' then Cassandra::Types.time
66
+ when 'tinyint' then Cassandra::Types.tinyint
67
+ when 'map' then Cassandra::Types.map(*node.children.map { |t| lookup_type(t, types)})
68
+ when 'set' then Cassandra::Types.set(lookup_type(node.children.first, types))
69
+ when 'list' then Cassandra::Types.list(lookup_type(node.children.first, types))
70
+ when 'tuple' then Cassandra::Types.tuple(*node.children.map { |t| lookup_type(t, types)})
71
+ when 'empty' then Cassandra::Types.custom('org.apache.cassandra.db.marshal.EmptyType')
72
+ else
73
+ types.fetch(node.name) { raise IncompleteTypeError, "unable to lookup type #{node.name.inspect}" }
74
+ end
75
+ end
76
+
77
+ def parse_node(string)
78
+ root = node = Node.new(nil, '', [])
79
+
80
+ string.each_char do |char|
81
+ case char
82
+ when '<' # starting type params
83
+ child = Node.new(node, '', [])
84
+ node.children << child
85
+ node = child
86
+ when ','
87
+ child = Node.new(node.parent, '', [])
88
+ node.parent.children << child
89
+ node = child
90
+ when '>'
91
+ node = node.parent
92
+ when ' '
93
+ next
94
+ else
95
+ node.name << char
96
+ end
97
+ end
98
+
99
+ root
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,1174 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2015 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ class Cluster
21
+ class Schema
22
+ # @private
23
+ module Fetcher
24
+ FUTURE_EMPTY_LIST = Ione::Future.resolved(EMPTY_LIST)
25
+ REPLICATION_PACKAGE_PREFIX = 'org.apache.cassandra.locator.'.freeze
26
+ COMPRESSION_PACKAGE_PREFIX = 'org.apache.cassandra.io.compress.'.freeze
27
+
28
+ def fetch(connection)
29
+ Ione::Future.all(select_keyspaces(connection),
30
+ select_tables(connection),
31
+ select_columns(connection),
32
+ select_types(connection),
33
+ select_functions(connection),
34
+ select_aggregates(connection))
35
+ .map do |(rows_keyspaces, rows_tables, rows_columns,
36
+ rows_types, rows_functions, rows_aggregates)|
37
+
38
+ lookup_tables = map_rows_by(rows_tables, 'keyspace_name')
39
+ lookup_columns = map_rows_by(rows_columns, 'keyspace_name')
40
+ lookup_types = map_rows_by(rows_types, 'keyspace_name')
41
+ lookup_functions = map_rows_by(rows_functions, 'keyspace_name')
42
+ lookup_aggregates = map_rows_by(rows_aggregates, 'keyspace_name')
43
+
44
+ rows_keyspaces.map do |keyspace_data|
45
+ name = keyspace_data['keyspace_name']
46
+
47
+ create_keyspace(keyspace_data,
48
+ lookup_tables[name],
49
+ lookup_columns[name],
50
+ lookup_types[name],
51
+ lookup_functions[name],
52
+ lookup_aggregates[name])
53
+ end
54
+ end
55
+ end
56
+
57
+ def fetch_keyspace(connection, keyspace_name)
58
+ Ione::Future.all(select_keyspace(connection, keyspace_name),
59
+ select_keyspace_tables(connection, keyspace_name),
60
+ select_keyspace_columns(connection, keyspace_name),
61
+ select_keyspace_types(connection, keyspace_name),
62
+ 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)|
66
+ if rows_keyspaces.empty?
67
+ nil
68
+ else
69
+ create_keyspace(rows_keyspaces.first,
70
+ rows_tables,
71
+ rows_columns,
72
+ rows_types,
73
+ rows_functions,
74
+ rows_aggregates)
75
+ end
76
+ end
77
+ end
78
+
79
+ def fetch_table(connection, keyspace_name, table_name)
80
+ 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)|
83
+ if rows_tables.empty?
84
+ nil
85
+ else
86
+ create_table(rows_tables.first,
87
+ rows_columns)
88
+ end
89
+ end
90
+ end
91
+
92
+ def fetch_type(connection, keyspace_name, type_name)
93
+ select_type(connection, keyspace_name, type_name).map do |rows_types|
94
+ if rows_types.empty?
95
+ nil
96
+ else
97
+ create_type(rows_types.first)
98
+ end
99
+ end
100
+ end
101
+
102
+ def fetch_function(connection, keyspace_name, function_name, function_args)
103
+ select_function(connection, keyspace_name, function_name, function_args).map do |rows_functions|
104
+ if rows_functions.empty?
105
+ nil
106
+ else
107
+ create_function(rows_functions.first)
108
+ end
109
+ end
110
+ end
111
+
112
+ def fetch_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
113
+ select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args).map do |rows_aggregates|
114
+ if rows_aggregates.empty?
115
+ nil
116
+ else
117
+ create_aggregate(rows_aggregates.first, @schema.keyspace(keyspace_name).send(:raw_functions))
118
+ end
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def select_keyspaces(connection)
125
+ FUTURE_EMPTY_LIST
126
+ end
127
+
128
+ def select_tables(connection)
129
+ FUTURE_EMPTY_LIST
130
+ end
131
+
132
+ def select_columns(connection)
133
+ FUTURE_EMPTY_LIST
134
+ end
135
+
136
+ def select_types(connection)
137
+ FUTURE_EMPTY_LIST
138
+ end
139
+
140
+ def select_functions(connection)
141
+ FUTURE_EMPTY_LIST
142
+ end
143
+
144
+ def select_aggregates(connection)
145
+ FUTURE_EMPTY_LIST
146
+ end
147
+
148
+ def select_keyspace(connection, keyspace_name)
149
+ FUTURE_EMPTY_LIST
150
+ end
151
+
152
+ def select_keyspace_tables(connection, keyspace_name)
153
+ FUTURE_EMPTY_LIST
154
+ end
155
+
156
+ def select_keyspace_columns(connection, keyspace_name)
157
+ FUTURE_EMPTY_LIST
158
+ end
159
+
160
+ def select_keyspace_types(connection, keyspace_name)
161
+ FUTURE_EMPTY_LIST
162
+ end
163
+
164
+ def select_keyspace_functions(connection, keyspace_name)
165
+ FUTURE_EMPTY_LIST
166
+ end
167
+
168
+ def select_keyspace_aggregates(connection, keyspace_name)
169
+ FUTURE_EMPTY_LIST
170
+ end
171
+
172
+ def select_table(connection, keyspace_name, table_name)
173
+ FUTURE_EMPTY_LIST
174
+ end
175
+
176
+ def select_table_columns(connection, keyspace_name, table_name)
177
+ FUTURE_EMPTY_LIST
178
+ end
179
+
180
+ def select_type(connection, keyspace_name, type_name)
181
+ FUTURE_EMPTY_LIST
182
+ end
183
+
184
+ def select_function(connection, keyspace_name, function_name, function_args)
185
+ FUTURE_EMPTY_LIST
186
+ end
187
+
188
+ def select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
189
+ FUTURE_EMPTY_LIST
190
+ end
191
+
192
+ def send_select_request(connection, cql, params = EMPTY_LIST, types = EMPTY_LIST)
193
+ backtrace = caller
194
+ connection.send_request(Protocol::QueryRequest.new(cql, params, types, :one)).map do |r|
195
+ case r
196
+ when Protocol::RowsResultResponse
197
+ r.rows
198
+ when Protocol::ErrorResponse
199
+ e = r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0)
200
+ e.set_backtrace(backtrace)
201
+ raise e
202
+ else
203
+ raise Errors::InternalError, "Unexpected response #{r.inspect}", caller
204
+ end
205
+ end
206
+ end
207
+
208
+ def map_rows_by(rows, key_name, &block)
209
+ rows.each_with_object(::Hash.new { EMPTY_LIST }) do |row, map|
210
+ key = row[key_name]
211
+ map[key] = [] unless map.has_key?(key)
212
+
213
+ if block
214
+ map[key] << yield(row)
215
+ else
216
+ map[key] << row
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ # @private
223
+ module Fetchers
224
+ class V1_2_x
225
+ SELECT_KEYSPACES = 'SELECT * FROM system.schema_keyspaces'.freeze
226
+ SELECT_TABLES = 'SELECT * FROM system.schema_columnfamilies'.freeze
227
+ SELECT_COLUMNS = 'SELECT * FROM system.schema_columns'.freeze
228
+ SELECT_KEYSPACE = 'SELECT * FROM system.schema_keyspaces WHERE keyspace_name = \'%s\''.freeze
229
+ SELECT_KEYSPACE_TABLES = 'SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = \'%s\''.freeze
230
+ SELECT_KEYSPACE_COLUMNS = 'SELECT * FROM system.schema_columns WHERE keyspace_name = \'%s\''.freeze
231
+ SELECT_TABLE = 'SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = \'%s\' AND columnfamily_name = \'%s\''.freeze
232
+ SELECT_TABLE_COLUMNS = 'SELECT * FROM system.schema_columns WHERE keyspace_name = \'%s\' AND columnfamily_name = \'%s\''.freeze
233
+
234
+ include Fetcher
235
+
236
+ def initialize(type_parser, schema)
237
+ @type_parser = type_parser
238
+ @schema = schema
239
+ end
240
+
241
+ private
242
+
243
+ def select_keyspaces(connection)
244
+ send_select_request(connection, SELECT_KEYSPACES)
245
+ end
246
+
247
+ def select_tables(connection)
248
+ send_select_request(connection, SELECT_TABLES)
249
+ end
250
+
251
+ def select_columns(connection)
252
+ send_select_request(connection, SELECT_COLUMNS)
253
+ end
254
+
255
+ def select_keyspace(connection, keyspace_name)
256
+ send_select_request(connection, SELECT_KEYSPACE % keyspace_name)
257
+ end
258
+
259
+ def select_keyspace_tables(connection, keyspace_name)
260
+ send_select_request(connection, SELECT_KEYSPACE_TABLES % keyspace_name)
261
+ end
262
+
263
+ def select_keyspace_columns(connection, keyspace_name)
264
+ send_select_request(connection, SELECT_KEYSPACE_COLUMNS % keyspace_name)
265
+ end
266
+
267
+ def select_table(connection, keyspace_name, table_name)
268
+ send_select_request(connection, SELECT_TABLE % [keyspace_name, table_name])
269
+ end
270
+
271
+ def select_table_columns(connection, keyspace_name, table_name)
272
+ send_select_request(connection, SELECT_TABLE_COLUMNS % [keyspace_name, table_name])
273
+ end
274
+
275
+ def create_replication(keyspace_data)
276
+ klass = keyspace_data['strategy_class']
277
+ klass.slice!(REPLICATION_PACKAGE_PREFIX)
278
+ options = ::JSON.load(keyspace_data['strategy_options'])
279
+ Keyspace::Replication.new(klass, options)
280
+ end
281
+
282
+ def create_keyspace(keyspace_data, rows_tables, rows_columns,
283
+ rows_types, rows_functions, rows_aggregates)
284
+ keyspace_name = keyspace_data['keyspace_name']
285
+ replication = create_replication(keyspace_data)
286
+ types = rows_types.each_with_object({}) do |row, types|
287
+ types[row['type_name']] = create_type(row)
288
+ end
289
+
290
+ # We want the functions hash to be keyed on [name, arg-types-list].
291
+ # Similarly for the aggregates hash.
292
+
293
+ functions = rows_functions.each_with_object({}) do |row, collector|
294
+ func = create_function(row)
295
+ collector[[func.name, func.argument_types]] = func
296
+ end
297
+
298
+ aggregates = rows_aggregates.each_with_object({}) do |row, collector|
299
+ agg = create_aggregate(row, functions)
300
+ collector[[agg.name, agg.argument_types]] = agg
301
+ end
302
+
303
+ lookup_columns = map_rows_by(rows_columns, 'columnfamily_name')
304
+ tables = rows_tables.each_with_object({}) do |row, tables|
305
+ table_name = row['columnfamily_name']
306
+ tables[table_name] = create_table(row, lookup_columns[table_name])
307
+ end
308
+
309
+ Keyspace.new(keyspace_name, keyspace_data['durable_writes'],
310
+ replication, tables, types, functions, aggregates)
311
+ end
312
+
313
+ def create_table(table_data, rows_columns)
314
+ keyspace_name = table_data['keyspace_name']
315
+ table_name = table_data['columnfamily_name']
316
+ key_validator = @type_parser.parse(table_data['key_validator'])
317
+ comparator = @type_parser.parse(table_data['comparator'])
318
+ column_aliases = ::JSON.load(table_data['column_aliases'])
319
+
320
+ if !comparator.collections.nil?
321
+ size = comparator.results.size
322
+ if !comparator.collections.empty?
323
+ is_compact = false
324
+ has_value = false
325
+ clustering_size = size - 2
326
+ elsif column_aliases.size == size - 1 && comparator.results.last.first == Cassandra::Types.varchar
327
+ is_compact = false
328
+ has_value = false
329
+ clustering_size = size - 1
330
+ else
331
+ is_compact = true
332
+ has_value = (!column_aliases.empty? || rows_columns.empty?)
333
+ clustering_size = size
334
+ end
335
+ else
336
+ is_compact = true
337
+ if (!column_aliases.empty? || rows_columns.empty?)
338
+ has_value = true
339
+ clustering_size = comparator.results.size
340
+ else
341
+ has_value = false
342
+ clustering_size = 0
343
+ end
344
+ end
345
+
346
+ partition_key = []
347
+ clustering_columns = []
348
+ clustering_order = []
349
+
350
+ compaction_strategy = create_compaction_strategy(table_data)
351
+ table_options = create_table_options(table_data, compaction_strategy, is_compact)
352
+ table_columns = {}
353
+ other_columns = []
354
+
355
+ key_aliases = ::JSON.load(table_data['key_aliases'])
356
+
357
+ key_validator.results.each_with_index do |(type, order, is_frozen), i|
358
+ key_alias = key_aliases.fetch(i) { i.zero? ? "key" : "key#{i + 1}" }
359
+
360
+ partition_key[i] = Column.new(key_alias, type, order, nil, false, is_frozen)
361
+ end
362
+
363
+ clustering_size.times do |i|
364
+ column_alias = column_aliases.fetch(i) { "column#{i + 1}" }
365
+ type, order, is_frozen = comparator.results.fetch(i)
366
+
367
+ clustering_columns[i] = Column.new(column_alias, type, order, nil, false, is_frozen)
368
+ clustering_order[i] = order
369
+ end
370
+
371
+ if has_value
372
+ value_alias = table_data['value_alias']
373
+ value_alias ||= 'value'
374
+
375
+ unless value_alias.empty?
376
+ type, order, is_frozen = @type_parser.parse(table_data['default_validator']).results.first
377
+ other_columns << Column.new(value_alias, type, order, nil, false, is_frozen)
378
+ end
379
+ end
380
+
381
+ rows_columns.each do |row|
382
+ other_columns << create_column(row)
383
+ end
384
+
385
+ partition_key.each do |column|
386
+ table_columns[column.name] = column
387
+ end
388
+
389
+ clustering_columns.each do |column|
390
+ table_columns[column.name] = column
391
+ end
392
+
393
+ other_columns.each do |column|
394
+ table_columns[column.name] = column
395
+ end
396
+
397
+ Table.new(keyspace_name, table_name, partition_key, clustering_columns,
398
+ table_columns, table_options, clustering_order)
399
+ end
400
+
401
+ def create_column(column_data)
402
+ name = column_data['column_name']
403
+ is_static = (column_data['type'] == 'STATIC')
404
+ type, order, is_frozen = @type_parser.parse(column_data['validator']).results.first
405
+
406
+ if column_data['index_type'].nil?
407
+ index = nil
408
+ elsif column_data['index_type'].to_s.upcase == 'CUSTOM' || !column_data['index_options']
409
+ index = Column::Index.new(column_data['index_name'])
410
+ else
411
+ options = ::JSON.load(column_data['index_options'])
412
+ index = Column::Index.new(column_data['index_name'], options && options['class_name'])
413
+ end
414
+
415
+ Column.new(name, type, order, index, is_static, is_frozen)
416
+ end
417
+
418
+ def create_compaction_strategy(table_data)
419
+ klass = table_data['compaction_strategy_class']
420
+ klass.slice!('org.apache.cassandra.db.compaction.')
421
+ options = ::JSON.load(table_data['compaction_strategy_options'])
422
+ Table::Compaction.new(klass, options)
423
+ end
424
+
425
+ def create_table_options(table_data, compaction_strategy, is_compact)
426
+ compression_parameters = ::JSON.load(table_data['compression_parameters'])
427
+ compression_parameters['sstable_compression'].slice!(COMPRESSION_PACKAGE_PREFIX) if compression_parameters['sstable_compression']
428
+ Table::Options.new(
429
+ table_data['comment'],
430
+ table_data['read_repair_chance'],
431
+ table_data['local_read_repair_chance'],
432
+ table_data['gc_grace_seconds'],
433
+ table_data['caching'],
434
+ table_data['bloom_filter_fp_chance'] || 0.01,
435
+ table_data['populate_io_cache_on_flush'],
436
+ table_data['memtable_flush_period_in_ms'],
437
+ table_data['default_time_to_live'],
438
+ nil,
439
+ nil,
440
+ table_data['replicate_on_write'],
441
+ nil,
442
+ nil,
443
+ compaction_strategy,
444
+ compression_parameters,
445
+ is_compact
446
+ )
447
+ end
448
+ end
449
+
450
+ class V2_0_x < V1_2_x
451
+ SELECT_KEYSPACE = 'SELECT * FROM system.schema_keyspaces WHERE keyspace_name = ?'.freeze
452
+ SELECT_KEYSPACE_TABLES = 'SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ?'.freeze
453
+ SELECT_KEYSPACE_COLUMNS = 'SELECT * FROM system.schema_columns WHERE keyspace_name = ?'.freeze
454
+ SELECT_TABLE = 'SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
455
+ SELECT_TABLE_COLUMNS = 'SELECT * FROM system.schema_columns WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
456
+
457
+ private
458
+
459
+ def create_table(table_data, rows_columns)
460
+ keyspace_name = table_data['keyspace_name']
461
+ table_name = table_data['columnfamily_name']
462
+ comparator = @type_parser.parse(table_data['comparator'])
463
+ table_columns = {}
464
+ other_columns = []
465
+ clustering_size = 0
466
+
467
+ partition_key = []
468
+ clustering_columns = []
469
+ clustering_order = []
470
+
471
+ rows_columns.each do |row|
472
+ next if row['column_name'].empty?
473
+
474
+ column = create_column(row)
475
+ type = row['type'].to_s
476
+ index = row['component_index'] || 0
477
+
478
+ case type.upcase
479
+ when 'PARTITION_KEY'
480
+ partition_key[index] = column
481
+ when 'CLUSTERING_KEY'
482
+ clustering_columns[index] = column
483
+ clustering_order[index] = column.order
484
+
485
+ if clustering_size.zero? || index == clustering_size
486
+ clustering_size = index + 1
487
+ end
488
+ else
489
+ other_columns << column
490
+ end
491
+ end
492
+
493
+ partition_key.each do |column|
494
+ table_columns[column.name] = column
495
+ end
496
+
497
+ clustering_columns.each do |column|
498
+ table_columns[column.name] = column
499
+ end
500
+
501
+ other_columns.each do |column|
502
+ table_columns[column.name] = column
503
+ end
504
+
505
+ compaction_strategy = create_compaction_strategy(table_data)
506
+ is_compact = (clustering_size != comparator.results.size - 1) || !comparator.collections
507
+ table_options = create_table_options(table_data, compaction_strategy, is_compact)
508
+
509
+ Table.new(keyspace_name, table_name, partition_key, clustering_columns,
510
+ table_columns, table_options, clustering_order)
511
+ end
512
+
513
+ def select_keyspace(connection, keyspace_name)
514
+ params = [keyspace_name]
515
+ hints = [Types.varchar]
516
+ send_select_request(connection, SELECT_KEYSPACE, params, hints)
517
+ end
518
+
519
+ def select_keyspace_tables(connection, keyspace_name)
520
+ params = [keyspace_name]
521
+ hints = [Types.varchar]
522
+ send_select_request(connection, SELECT_KEYSPACE_TABLES, params, hints)
523
+ end
524
+
525
+ def select_keyspace_columns(connection, keyspace_name)
526
+ params = [keyspace_name]
527
+ hints = [Types.varchar]
528
+ send_select_request(connection, SELECT_KEYSPACE_COLUMNS, params, hints)
529
+ end
530
+
531
+ def select_table(connection, keyspace_name, table_name)
532
+ params = [keyspace_name, table_name]
533
+ hints = [Types.varchar, Types.varchar]
534
+ send_select_request(connection, SELECT_TABLE, params, hints)
535
+ end
536
+
537
+ def select_table_columns(connection, keyspace_name, table_name)
538
+ params = [keyspace_name, table_name]
539
+ hints = [Types.varchar, Types.varchar]
540
+ send_select_request(connection, SELECT_TABLE_COLUMNS, params, hints)
541
+ end
542
+
543
+ def create_table_options(table_data, compaction_strategy, is_compact)
544
+ compression_parameters = ::JSON.load(table_data['compression_parameters'])
545
+ compression_parameters['sstable_compression'].slice!(COMPRESSION_PACKAGE_PREFIX) if compression_parameters['sstable_compression']
546
+ Table::Options.new(
547
+ table_data['comment'],
548
+ table_data['read_repair_chance'],
549
+ table_data['local_read_repair_chance'],
550
+ table_data['gc_grace_seconds'],
551
+ table_data['caching'],
552
+ table_data['bloom_filter_fp_chance'],
553
+ table_data['populate_io_cache_on_flush'],
554
+ table_data['memtable_flush_period_in_ms'],
555
+ table_data['default_time_to_live'],
556
+ table_data['speculative_retry'],
557
+ table_data['index_interval'],
558
+ table_data['replicate_on_write'],
559
+ nil,
560
+ nil,
561
+ compaction_strategy,
562
+ compression_parameters,
563
+ is_compact
564
+ )
565
+ end
566
+ end
567
+
568
+ class V2_1_x < V2_0_x
569
+ SELECT_TYPES = 'SELECT * FROM system.schema_usertypes'.freeze
570
+ SELECT_KEYSPACE_TYPES = 'SELECT * FROM system.schema_usertypes WHERE keyspace_name = ?'.freeze
571
+ SELECT_TYPE = 'SELECT * FROM system.schema_usertypes WHERE keyspace_name = ? AND type_name = ?'.freeze
572
+
573
+ private
574
+
575
+ def create_type(type_data)
576
+ keyspace_name = type_data['keyspace_name']
577
+ type_name = type_data['type_name']
578
+ type_fields = ::Array.new
579
+
580
+ field_names = type_data['field_names']
581
+ field_types = type_data['field_types']
582
+
583
+ field_names.zip(field_types) do |(field_name, fqcn)|
584
+ field_type = @type_parser.parse(fqcn).results.first.first
585
+
586
+ type_fields << [field_name, field_type]
587
+ end
588
+
589
+ Types.udt(keyspace_name, type_name, type_fields)
590
+ end
591
+
592
+ def select_types(connection)
593
+ send_select_request(connection, SELECT_TYPES)
594
+ end
595
+
596
+ def select_keyspace_types(connection, keyspace_name)
597
+ params = [keyspace_name]
598
+ hints = [Types.varchar]
599
+ send_select_request(connection, SELECT_KEYSPACE_TYPES, params, hints)
600
+ end
601
+
602
+ def select_type(connection, keyspace_name, type_name)
603
+ params = [keyspace_name, type_name]
604
+ hints = [Types.varchar, Types.varchar]
605
+ send_select_request(connection, SELECT_TYPE, params, hints)
606
+ end
607
+
608
+ def create_table_options(table_data, compaction_strategy, is_compact)
609
+ compression_parameters = ::JSON.load(table_data['compression_parameters'])
610
+ compression_parameters['sstable_compression'].slice!(COMPRESSION_PACKAGE_PREFIX) if compression_parameters['sstable_compression']
611
+ Table::Options.new(
612
+ table_data['comment'],
613
+ table_data['read_repair_chance'],
614
+ table_data['local_read_repair_chance'],
615
+ table_data['gc_grace_seconds'],
616
+ table_data['caching'],
617
+ table_data['bloom_filter_fp_chance'],
618
+ table_data['populate_io_cache_on_flush'],
619
+ table_data['memtable_flush_period_in_ms'],
620
+ table_data['default_time_to_live'],
621
+ table_data['speculative_retry'],
622
+ table_data['index_interval'],
623
+ table_data['replicate_on_write'],
624
+ table_data['min_index_interval'],
625
+ table_data['max_index_interval'],
626
+ compaction_strategy,
627
+ compression_parameters,
628
+ is_compact
629
+ )
630
+ end
631
+ end
632
+
633
+ class V2_2_x < V2_1_x
634
+ SELECT_FUNCTIONS = 'SELECT * FROM system.schema_functions'.freeze
635
+ SELECT_AGGREGATES = 'SELECT * FROM system.schema_aggregates'.freeze
636
+ SELECT_KEYSPACE_FUNCTIONS = 'SELECT * FROM system.schema_functions WHERE keyspace_name = ?'.freeze
637
+ SELECT_KEYSPACE_AGGREGATES = 'SELECT * FROM system.schema_aggregates WHERE keyspace_name = ?'.freeze
638
+ SELECT_FUNCTION = 'SELECT * FROM system.schema_functions WHERE keyspace_name = ? AND function_name = ? AND argument_types = ?'.freeze
639
+ SELECT_AGGREGATE = 'SELECT * FROM system.schema_aggregates WHERE keyspace_name = ? AND aggregate_name = ? AND argument_types = ?'.freeze
640
+
641
+ # parse an array of string argument types and return an array of [Cassandra::Type]s.
642
+ # @param connection a connection to a Cassandra node.
643
+ # @param keyspace_name [String] name of the keyspace.
644
+ # @param argument_types [Array<String>] array of argument types.
645
+ # @return [Array<Cassandra::Type>] array of parsed types.
646
+ def parse_argument_types(connection, keyspace_name, argument_types)
647
+ argument_types.map do |argument_type|
648
+ @type_parser.parse(argument_type).results.first.first
649
+ end
650
+ end
651
+
652
+ private
653
+
654
+ def create_function(function_data)
655
+ keyspace_name = function_data['keyspace_name']
656
+ function_name = function_data['function_name']
657
+ function_lang = function_data['language']
658
+ function_type = @type_parser.parse(function_data['return_type']).results.first.first
659
+ function_body = function_data['body']
660
+ called_on_null = function_data['called_on_null_input']
661
+
662
+ arguments = []
663
+
664
+ Array(function_data['argument_names']).zip(Array(function_data['argument_types'])) do |argument_name, fqcn|
665
+ argument_type = @type_parser.parse(fqcn).results.first.first
666
+ arguments << Argument.new(argument_name, argument_type)
667
+ end
668
+
669
+ Function.new(keyspace_name, function_name, function_lang, function_type, arguments, function_body, called_on_null)
670
+ end
671
+
672
+ def create_aggregate(aggregate_data, functions)
673
+ keyspace_name = aggregate_data['keyspace_name']
674
+ aggregate_name = aggregate_data['aggregate_name']
675
+ aggregate_type = @type_parser.parse(aggregate_data['return_type']).results.first.first
676
+ argument_types = aggregate_data['argument_types'].map {|fqcn| @type_parser.parse(fqcn).results.first.first}.freeze
677
+ state_type = @type_parser.parse(aggregate_data['state_type']).results.first.first
678
+ initial_state = Util.encode_object(Protocol::Coder.read_value_v4(Protocol::CqlByteBuffer.new.append_bytes(aggregate_data['initcond']), state_type))
679
+
680
+ # The state-function takes arguments: first the stype, then the args of the aggregate.
681
+ state_function = functions[[aggregate_data['state_func'], [state_type].concat(argument_types)]]
682
+
683
+ # The final-function takes an stype argument.
684
+ final_function = functions[[aggregate_data['final_func'], [state_type]]]
685
+
686
+ Aggregate.new(keyspace_name, aggregate_name, aggregate_type, argument_types, state_type, initial_state, state_function, final_function)
687
+ end
688
+
689
+ def select_functions(connection)
690
+ send_select_request(connection, SELECT_FUNCTIONS)
691
+ end
692
+
693
+ def select_aggregates(connection)
694
+ send_select_request(connection, SELECT_AGGREGATES)
695
+ end
696
+
697
+ def select_keyspace_functions(connection, keyspace_name)
698
+ params = [keyspace_name]
699
+ hints = [Types.varchar]
700
+ send_select_request(connection, SELECT_KEYSPACE_FUNCTIONS, params, hints)
701
+ end
702
+
703
+ def select_keyspace_aggregates(connection, keyspace_name)
704
+ params = [keyspace_name]
705
+ hints = [Types.varchar]
706
+ send_select_request(connection, SELECT_KEYSPACE_AGGREGATES, params, hints)
707
+ end
708
+
709
+ def select_function(connection, keyspace_name, function_name, function_args)
710
+ params = [keyspace_name, function_name, function_args.map(&:to_s)]
711
+ hints = [Types.varchar, Types.varchar, Types.list(Types.varchar)]
712
+ send_select_request(connection, SELECT_FUNCTION, params, hints)
713
+ end
714
+
715
+ def select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
716
+ params = [keyspace_name, aggregate_name, aggregate_args.map(&:to_s)]
717
+ hints = [Types.varchar, Types.varchar, Types.list(Types.varchar)]
718
+ send_select_request(connection, SELECT_AGGREGATE, params, hints)
719
+ end
720
+ end
721
+
722
+ class V3_0_x < V2_2_x
723
+ SELECT_KEYSPACES = "SELECT * FROM system_schema.keyspaces".freeze
724
+ SELECT_TABLES = "SELECT * FROM system_schema.tables".freeze
725
+ SELECT_COLUMNS = "SELECT * FROM system_schema.columns".freeze
726
+ SELECT_TYPES = "SELECT * FROM system_schema.types".freeze
727
+ SELECT_FUNCTIONS = "SELECT * FROM system_schema.functions".freeze
728
+ SELECT_AGGREGATES = "SELECT * FROM system_schema.aggregates".freeze
729
+ SELECT_INDEXES = "SELECT * FROM system_schema.indexes".freeze
730
+ SELECT_VIEWS = "SELECT * FROM system_schema.materialized_views".freeze
731
+
732
+ SELECT_KEYSPACE = 'SELECT * FROM system_schema.keyspaces WHERE keyspace_name = ?'.freeze
733
+ SELECT_KEYSPACE_TABLES = 'SELECT * FROM system_schema.tables WHERE keyspace_name = ?'.freeze
734
+ SELECT_KEYSPACE_COLUMNS = 'SELECT * FROM system_schema.columns WHERE keyspace_name = ?'.freeze
735
+ SELECT_KEYSPACE_TYPES = "SELECT * FROM system_schema.types WHERE keyspace_name = ?".freeze
736
+ SELECT_KEYSPACE_FUNCTIONS = 'SELECT * FROM system_schema.functions WHERE keyspace_name = ?'.freeze
737
+ SELECT_KEYSPACE_AGGREGATES = 'SELECT * FROM system_schema.aggregates WHERE keyspace_name = ?'.freeze
738
+
739
+ SELECT_TABLE = 'SELECT * FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?'.freeze
740
+ SELECT_TABLE_COLUMNS = 'SELECT * FROM system_schema.columns WHERE keyspace_name = ? AND table_name = ?'.freeze
741
+
742
+ SELECT_TYPE = 'SELECT * FROM system_schema.types WHERE keyspace_name = ? AND type_name = ?'.freeze
743
+
744
+ SELECT_FUNCTION = 'SELECT * FROM system_schema.functions WHERE keyspace_name = ? AND function_name = ? AND argument_types = ?'.freeze
745
+
746
+ SELECT_AGGREGATE = 'SELECT * FROM system_schema.aggregates WHERE keyspace_name = ? AND aggregate_name = ? AND argument_types = ?'.freeze
747
+
748
+ # parse an array of string argument types and return an array of [Cassandra::Type]s.
749
+ # @param connection a connection to a Cassandra node.
750
+ # @param keyspace_name [String] name of the keyspace.
751
+ # @param argument_types [Array<String>] array of argument types.
752
+ # @return [Array<Cassandra::Type>] array of parsed types.
753
+ def parse_argument_types(connection, keyspace_name, argument_types)
754
+ types = @schema.keyspace(keyspace_name).send(:raw_types)
755
+ argument_types.map do |argument_type|
756
+ @type_parser.parse(argument_type, types).first
757
+ end
758
+ end
759
+
760
+ private
761
+
762
+ def select_keyspaces(connection)
763
+ send_select_request(connection, SELECT_KEYSPACES)
764
+ end
765
+
766
+ def select_tables(connection)
767
+ send_select_request(connection, SELECT_TABLES)
768
+ end
769
+
770
+ def select_columns(connection)
771
+ send_select_request(connection, SELECT_COLUMNS)
772
+ end
773
+
774
+ def select_types(connection)
775
+ send_select_request(connection, SELECT_TYPES)
776
+ end
777
+
778
+ def select_functions(connection)
779
+ send_select_request(connection, SELECT_FUNCTIONS)
780
+ end
781
+
782
+ def select_aggregates(connection)
783
+ send_select_request(connection, SELECT_AGGREGATES)
784
+ end
785
+
786
+ def select_keyspace(connection, keyspace_name)
787
+ params = [keyspace_name]
788
+ hints = [Types.varchar]
789
+ send_select_request(connection, SELECT_KEYSPACE, params, hints)
790
+ end
791
+
792
+ def select_keyspace_tables(connection, keyspace_name)
793
+ params = [keyspace_name]
794
+ hints = [Types.varchar]
795
+ send_select_request(connection, SELECT_KEYSPACE_TABLES, params, hints)
796
+ end
797
+
798
+ def select_keyspace_columns(connection, keyspace_name)
799
+ params = [keyspace_name]
800
+ hints = [Types.varchar]
801
+ send_select_request(connection, SELECT_KEYSPACE_COLUMNS, params, hints)
802
+ end
803
+
804
+ def select_keyspace_types(connection, keyspace_name)
805
+ params = [keyspace_name]
806
+ hints = [Types.varchar]
807
+ send_select_request(connection, SELECT_KEYSPACE_TYPES, params, hints)
808
+ end
809
+
810
+ def select_keyspace_functions(connection, keyspace_name)
811
+ params = [keyspace_name]
812
+ hints = [Types.varchar]
813
+ send_select_request(connection, SELECT_KEYSPACE_FUNCTIONS, params, hints)
814
+ end
815
+
816
+ def select_keyspace_aggregates(connection, keyspace_name)
817
+ params = [keyspace_name]
818
+ hints = [Types.varchar]
819
+ send_select_request(connection, SELECT_KEYSPACE_AGGREGATES, params, hints)
820
+ end
821
+
822
+ def select_table(connection, keyspace_name, table_name)
823
+ params = [keyspace_name, table_name]
824
+ hints = [Types.varchar, Types.varchar]
825
+ send_select_request(connection, SELECT_TABLE, params, hints)
826
+ end
827
+
828
+ def select_table_columns(connection, keyspace_name, table_name)
829
+ params = [keyspace_name, table_name]
830
+ hints = [Types.varchar, Types.varchar]
831
+ send_select_request(connection, SELECT_TABLE_COLUMNS, params, hints)
832
+ end
833
+
834
+ def select_type(connection, keyspace_name, type_name)
835
+ params = [keyspace_name, type_name]
836
+ hints = [Types.varchar, Types.varchar]
837
+ send_select_request(connection, SELECT_TYPE, params, hints)
838
+ end
839
+
840
+ def select_function(connection, keyspace_name, function_name, function_args)
841
+ params = [keyspace_name, function_name, function_args.map(&:to_s)]
842
+ hints = [Types.varchar, Types.varchar, Types.list(Types.varchar)]
843
+ send_select_request(connection, SELECT_FUNCTION, params, hints)
844
+ end
845
+
846
+ def select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
847
+ params = [keyspace_name, aggregate_name, aggregate_args.map(&:to_s)]
848
+ hints = [Types.varchar, Types.varchar, Types.list(Types.varchar)]
849
+ send_select_request(connection, SELECT_AGGREGATE, params, hints)
850
+ end
851
+
852
+ def create_function(function_data, types = nil)
853
+ keyspace_name = function_data['keyspace_name']
854
+ function_name = function_data['function_name']
855
+ function_lang = function_data['language']
856
+ types ||= @schema.keyspace(keyspace_name).send(:raw_types)
857
+ function_type = @type_parser.parse(function_data['return_type'], types).first
858
+ function_body = function_data['body']
859
+ called_on_null = function_data['called_on_null_input']
860
+
861
+ arguments = []
862
+
863
+ function_data['argument_names'].zip(function_data['argument_types']) do |argument_name, argument_type|
864
+ arguments << Argument.new(argument_name, @type_parser.parse(argument_type, types).first)
865
+ end
866
+
867
+ Function.new(keyspace_name, function_name, function_lang, function_type, arguments, function_body, called_on_null)
868
+ end
869
+
870
+ def create_aggregate(aggregate_data, functions, types = nil)
871
+ keyspace_name = aggregate_data['keyspace_name']
872
+ aggregate_name = aggregate_data['aggregate_name']
873
+ types ||= @schema.keyspace(keyspace_name).send(:raw_types)
874
+ aggregate_type = @type_parser.parse(aggregate_data['return_type'], types).first
875
+ argument_types = aggregate_data['argument_types'].map {|argument_type| @type_parser.parse(argument_type, types).first}.freeze
876
+ state_type = @type_parser.parse(aggregate_data['state_type'], types).first
877
+ initial_state = aggregate_data['initcond'] || 'null'
878
+
879
+ # The state-function takes arguments: first the stype, then the args of the aggregate.
880
+ state_function = functions[[aggregate_data['state_func'], [state_type].concat(argument_types)]]
881
+
882
+ # The final-function takes an stype argument.
883
+ final_function = functions[[aggregate_data['final_func'], [state_type]]]
884
+
885
+ Aggregate.new(keyspace_name, aggregate_name, aggregate_type, argument_types, state_type, initial_state, state_function, final_function)
886
+ end
887
+
888
+ def create_types(rows_types, types)
889
+ skipped_rows = ::Array.new
890
+
891
+ loop do
892
+ rows_size = rows_types.size
893
+
894
+ until rows_types.empty?
895
+ type_data = rows_types.shift
896
+ type_name = type_data['type_name']
897
+ type_keyspace = type_data['keyspace_name']
898
+ type_fields = ::Array.new
899
+
900
+ begin
901
+ field_names = type_data['field_names']
902
+ field_types = type_data['field_types']
903
+ field_names.each_with_index do |field_name, i|
904
+ field_type = @type_parser.parse(field_types[i], types).first
905
+ type_fields << [field_name, field_type]
906
+ end
907
+
908
+ types[type_name] = Types.udt(type_keyspace, type_name, type_fields)
909
+ rescue CQLTypeParser::IncompleteTypeError
910
+ skipped_rows << type_data
911
+ next
912
+ end
913
+ end
914
+
915
+ break if skipped_rows.empty?
916
+
917
+ if rows_size == skipped_rows.size
918
+ raise "Unable to resolve circular references among UDTs when parsing"
919
+ else
920
+ rows_types, skipped_rows = skipped_rows, rows_types
921
+ end
922
+ end
923
+ end
924
+
925
+ def create_keyspace(keyspace_data, rows_tables, rows_columns,
926
+ rows_types, rows_functions, rows_aggregates)
927
+ keyspace_name = keyspace_data['keyspace_name']
928
+ replication = create_replication(keyspace_data)
929
+
930
+ types = ::Hash.new
931
+ create_types(rows_types, types)
932
+
933
+ # We want the functions hash to be keyed on [name, arg-types-list].
934
+ # Similarly for the aggregates hash.
935
+
936
+ functions = rows_functions.each_with_object({}) do |row, collector|
937
+ func = create_function(row, types)
938
+ collector[[func.name, func.argument_types]] = func
939
+ end
940
+
941
+ aggregates = rows_aggregates.each_with_object({}) do |row, collector|
942
+ agg = create_aggregate(row, functions, types)
943
+ collector[[agg.name, agg.argument_types]] = agg
944
+ end
945
+
946
+ lookup_columns = map_rows_by(rows_columns, 'table_name')
947
+ tables = rows_tables.each_with_object({}) do |row, tables|
948
+ table_name = row['table_name']
949
+ tables[table_name] = create_table(row, lookup_columns[table_name], types)
950
+ end
951
+
952
+ Keyspace.new(keyspace_name, keyspace_data['durable_writes'],
953
+ replication, tables, types, functions, aggregates)
954
+ end
955
+
956
+ def create_replication(keyspace_data)
957
+ options = keyspace_data['replication']
958
+ klass = options.delete('class')
959
+ klass.slice!(REPLICATION_PACKAGE_PREFIX)
960
+ Keyspace::Replication.new(klass, options)
961
+ end
962
+
963
+ def create_compaction_strategy(table_data)
964
+ options = table_data['compaction']
965
+ klass = options.delete('class')
966
+ klass.slice!('org.apache.cassandra.db.compaction.')
967
+ Table::Compaction.new(klass, options)
968
+ end
969
+
970
+ def create_table_options(table_data, compaction_strategy, is_compact)
971
+ compression = table_data['compression']
972
+ compression['class'].slice!(COMPRESSION_PACKAGE_PREFIX) if compression['class']
973
+
974
+ Table::Options.new(
975
+ table_data['comment'],
976
+ table_data['read_repair_chance'],
977
+ table_data['dclocal_read_repair_chance'],
978
+ table_data['gc_grace_seconds'],
979
+ table_data['caching'],
980
+ table_data['bloom_filter_fp_chance'],
981
+ nil,
982
+ table_data['memtable_flush_period_in_ms'],
983
+ table_data['default_time_to_live'],
984
+ table_data['speculative_retry'],
985
+ nil,
986
+ nil,
987
+ table_data['min_index_interval'],
988
+ table_data['max_index_interval'],
989
+ compaction_strategy,
990
+ compression,
991
+ is_compact
992
+ )
993
+ end
994
+
995
+ def create_column(column_data, types)
996
+ name = column_data['column_name']
997
+ is_static = (column_data['kind'].to_s.upcase == 'STATIC')
998
+ order = column_data['clustering_order'] == 'desc' ? :desc : :asc
999
+ type, is_frozen = @type_parser.parse(column_data['type'], types)
1000
+
1001
+ Column.new(name, type, order, nil, is_static, is_frozen)
1002
+ end
1003
+
1004
+ def create_table(table_data, rows_columns, types = nil)
1005
+ keyspace_name = table_data['keyspace_name']
1006
+ table_name = table_data['table_name']
1007
+ table_flags = table_data['flags']
1008
+ table_columns = {}
1009
+ other_columns = []
1010
+ clustering_size = 0
1011
+
1012
+ is_dense = table_flags.include?('dense')
1013
+ is_super = table_flags.include?('super')
1014
+ is_compound = table_flags.include?('compound')
1015
+ is_compact = is_super || is_dense || !is_compound
1016
+
1017
+ partition_key = []
1018
+ clustering_columns = []
1019
+ clustering_order = []
1020
+ types ||= @schema.keyspace(keyspace_name).send(:raw_types)
1021
+
1022
+ rows_columns.each do |row|
1023
+ next if row['column_name'].empty?
1024
+
1025
+ column = create_column(row, types)
1026
+ kind = row['kind'].to_s
1027
+ index = row['position'] || 0
1028
+
1029
+ case kind.upcase
1030
+ when 'PARTITION_KEY'
1031
+ partition_key[index] = column
1032
+ when 'CLUSTERING'
1033
+ clustering_columns[index] = column
1034
+ clustering_order[index] = column.order
1035
+
1036
+ if clustering_size.zero? || index == clustering_size
1037
+ clustering_size = index + 1
1038
+ end
1039
+ else
1040
+ other_columns << column
1041
+ end
1042
+ end
1043
+
1044
+ partition_key.each do |column|
1045
+ table_columns[column.name] = column
1046
+ end
1047
+
1048
+ clustering_columns.each do |column|
1049
+ table_columns[column.name] = column
1050
+ end
1051
+
1052
+ other_columns.each do |column|
1053
+ table_columns[column.name] = column
1054
+ end
1055
+
1056
+ compaction_strategy = create_compaction_strategy(table_data)
1057
+ table_options = create_table_options(table_data, compaction_strategy, is_compact)
1058
+
1059
+ Table.new(keyspace_name, table_name, partition_key, clustering_columns,
1060
+ table_columns, table_options, clustering_order)
1061
+ end
1062
+ end
1063
+
1064
+ class MultiVersion
1065
+ class Version
1066
+ def initialize(version, constructor)
1067
+ @version = version
1068
+ @constructor = constructor
1069
+ @fetcher = nil
1070
+ end
1071
+
1072
+ def matches?(version)
1073
+ version.start_with?(@version)
1074
+ end
1075
+
1076
+ def fetcher
1077
+ @fetcher ||= @constructor.call
1078
+ end
1079
+ end
1080
+
1081
+ def initialize(registry)
1082
+ @registry = registry
1083
+ @versions = []
1084
+ @fetchers = {}
1085
+ end
1086
+
1087
+ def when(version, &block)
1088
+ @versions << Version.new(version, block)
1089
+ end
1090
+
1091
+ def fetch(connection)
1092
+ find_fetcher(connection)
1093
+ .fetch(connection)
1094
+ rescue => e
1095
+ return Ione::Future.failed(e)
1096
+ end
1097
+
1098
+ def fetch_keyspace(connection, keyspace_name)
1099
+ find_fetcher(connection)
1100
+ .fetch_keyspace(connection, keyspace_name)
1101
+ rescue => e
1102
+ return Ione::Future.failed(e)
1103
+ end
1104
+
1105
+ def fetch_table(connection, keyspace_name, table_name)
1106
+ find_fetcher(connection)
1107
+ .fetch_table(connection, keyspace_name, table_name)
1108
+ rescue => e
1109
+ return Ione::Future.failed(e)
1110
+ end
1111
+
1112
+ def fetch_type(connection, keyspace_name, type_name)
1113
+ find_fetcher(connection)
1114
+ .fetch_type(connection, keyspace_name, type_name)
1115
+ rescue => e
1116
+ return Ione::Future.failed(e)
1117
+ end
1118
+
1119
+ def fetch_function(connection, keyspace_name, function_name, function_args)
1120
+ find_fetcher(connection)
1121
+ .fetch_function(connection, keyspace_name, function_name, function_args)
1122
+ rescue => e
1123
+ return Ione::Future.failed(e)
1124
+ end
1125
+
1126
+ def fetch_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
1127
+ find_fetcher(connection)
1128
+ .fetch_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
1129
+ rescue => e
1130
+ return Ione::Future.failed(e)
1131
+ end
1132
+
1133
+ # parse an array of string argument types and return an array of [Cassandra::Type]s.
1134
+ # @param connection a connection to a Cassandra node.
1135
+ # @param keyspace_name [String] name of the keyspace.
1136
+ # @param argument_types [Array<String>] array of argument types.
1137
+ # @return [Array<Cassandra::Type>] array of parsed types.
1138
+ def parse_argument_types(connection, keyspace_name, argument_types)
1139
+ find_fetcher(connection).parse_argument_types(connection, keyspace_name, argument_types)
1140
+ end
1141
+
1142
+ private
1143
+
1144
+ def find_fetcher(connection)
1145
+ host = @registry.host(connection.host)
1146
+
1147
+ unless host
1148
+ ips = @registry.hosts.map(&:ip)
1149
+ raise Errors::ClientError,
1150
+ "unable to find release version for current host, " \
1151
+ "connected to #{connection.host}, but cluster contains " \
1152
+ "#{ips}."
1153
+ end
1154
+
1155
+ version = host.release_version
1156
+ unless version
1157
+ raise Errors::ClientError, "unable to determine release " \
1158
+ "version for host: #{host.inspect}"
1159
+ end
1160
+
1161
+ @fetchers[version] ||= begin
1162
+ current = @versions.find {|v| v.matches?(version)}
1163
+ unless current
1164
+ raise Errors::ClientError, "unsupported release version " \
1165
+ "#{version.inspect}."
1166
+ end
1167
+ current.fetcher
1168
+ end
1169
+ end
1170
+ end
1171
+ end
1172
+ end
1173
+ end
1174
+ end