cassandra-driver 2.1.7 → 3.0.0.beta.1

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 (78) hide show
  1. checksums.yaml +8 -8
  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/datastax/cassandra.rb +5 -2
  78. metadata +16 -6
@@ -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