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.
- checksums.yaml +8 -8
- data/README.md +31 -53
- data/lib/cassandra.rb +22 -3
- data/lib/cassandra/aggregate.rb +109 -0
- data/lib/cassandra/argument.rb +51 -0
- data/lib/cassandra/auth/providers/password.rb +7 -4
- data/lib/cassandra/cluster.rb +14 -3
- data/lib/cassandra/cluster/client.rb +56 -34
- data/lib/cassandra/cluster/connector.rb +6 -6
- data/lib/cassandra/cluster/control_connection.rb +204 -251
- data/lib/cassandra/cluster/metadata.rb +2 -0
- data/lib/cassandra/cluster/schema.rb +131 -209
- data/lib/cassandra/cluster/schema/cql_type_parser.rb +104 -0
- data/lib/cassandra/cluster/schema/fetchers.rb +1174 -0
- data/lib/cassandra/cluster/schema/{type_parser.rb → fqcn_type_parser.rb} +7 -3
- data/lib/cassandra/column.rb +2 -2
- data/lib/cassandra/driver.rb +27 -9
- data/lib/cassandra/errors.rb +179 -25
- data/lib/cassandra/execution/info.rb +8 -1
- data/lib/cassandra/execution/options.rb +34 -0
- data/lib/cassandra/execution/trace.rb +42 -10
- data/lib/cassandra/function.rb +150 -0
- data/lib/cassandra/future.rb +66 -35
- data/lib/cassandra/host.rb +7 -4
- data/lib/cassandra/keyspace.rb +112 -13
- data/lib/cassandra/load_balancing.rb +1 -1
- data/lib/cassandra/protocol.rb +9 -3
- data/lib/cassandra/protocol/coder.rb +434 -155
- data/lib/cassandra/protocol/cql_byte_buffer.rb +43 -0
- data/lib/cassandra/protocol/cql_protocol_handler.rb +4 -1
- data/lib/cassandra/protocol/request.rb +4 -0
- data/lib/cassandra/protocol/requests/auth_response_request.rb +5 -1
- data/lib/cassandra/protocol/requests/batch_request.rb +7 -2
- data/lib/cassandra/protocol/requests/credentials_request.rb +5 -1
- data/lib/cassandra/protocol/requests/execute_request.rb +16 -10
- data/lib/cassandra/protocol/requests/prepare_request.rb +12 -3
- data/lib/cassandra/protocol/requests/query_request.rb +20 -11
- data/lib/cassandra/protocol/responses/already_exists_error_response.rb +4 -4
- data/lib/cassandra/protocol/responses/error_response.rb +14 -14
- data/lib/cassandra/protocol/responses/function_failure_error_response.rb +41 -0
- data/lib/cassandra/protocol/responses/prepared_result_response.rb +12 -9
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +5 -3
- data/lib/cassandra/protocol/responses/read_failure_error_response.rb +43 -0
- data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +4 -4
- data/lib/cassandra/protocol/responses/ready_response.rb +5 -1
- data/lib/cassandra/protocol/responses/result_response.rb +3 -3
- data/lib/cassandra/protocol/responses/rows_result_response.rb +2 -2
- data/lib/cassandra/protocol/responses/schema_change_event_response.rb +25 -24
- data/lib/cassandra/protocol/responses/schema_change_result_response.rb +20 -23
- data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +2 -2
- data/lib/cassandra/protocol/responses/unavailable_error_response.rb +4 -4
- data/lib/cassandra/protocol/responses/unprepared_error_response.rb +4 -4
- data/lib/cassandra/protocol/responses/write_failure_error_response.rb +45 -0
- data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +4 -4
- data/lib/cassandra/protocol/v1.rb +38 -13
- data/lib/cassandra/protocol/v3.rb +34 -29
- data/lib/cassandra/protocol/v4.rb +334 -0
- data/lib/cassandra/result.rb +10 -9
- data/lib/cassandra/retry.rb +17 -3
- data/lib/cassandra/retry/policies/default.rb +9 -3
- data/lib/cassandra/session.rb +15 -7
- data/lib/cassandra/statement.rb +5 -0
- data/lib/cassandra/statements/batch.rb +36 -12
- data/lib/cassandra/statements/bound.rb +2 -1
- data/lib/cassandra/statements/prepared.rb +106 -35
- data/lib/cassandra/statements/simple.rb +4 -2
- data/lib/cassandra/table.rb +70 -105
- data/lib/cassandra/time.rb +98 -0
- data/lib/cassandra/time_uuid.rb +1 -1
- data/lib/cassandra/tuple.rb +7 -0
- data/lib/cassandra/types.rb +472 -272
- data/lib/cassandra/udt.rb +10 -0
- data/lib/cassandra/util.rb +32 -1
- data/lib/cassandra/uuid.rb +6 -1
- data/lib/cassandra/uuid/generator.rb +7 -7
- data/lib/cassandra/version.rb +1 -1
- data/lib/datastax/cassandra.rb +5 -2
- 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
|