yugabyte-ycql-driver 3.2.3.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 +7 -0
- data/.yardopts +13 -0
- data/README.md +242 -0
- data/ext/cassandra_murmur3/cassandra_murmur3.c +178 -0
- data/ext/cassandra_murmur3/extconf.rb +2 -0
- data/lib/cassandra/address_resolution.rb +36 -0
- data/lib/cassandra/address_resolution/policies.rb +2 -0
- data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
- data/lib/cassandra/address_resolution/policies/none.rb +35 -0
- data/lib/cassandra/aggregate.rb +123 -0
- data/lib/cassandra/argument.rb +51 -0
- data/lib/cassandra/attr_boolean.rb +33 -0
- data/lib/cassandra/auth.rb +100 -0
- data/lib/cassandra/auth/providers.rb +17 -0
- data/lib/cassandra/auth/providers/password.rb +65 -0
- data/lib/cassandra/cassandra_logger.rb +80 -0
- data/lib/cassandra/cluster.rb +331 -0
- data/lib/cassandra/cluster/client.rb +1612 -0
- data/lib/cassandra/cluster/connection_pool.rb +78 -0
- data/lib/cassandra/cluster/connector.rb +372 -0
- data/lib/cassandra/cluster/control_connection.rb +962 -0
- data/lib/cassandra/cluster/failed_connection.rb +35 -0
- data/lib/cassandra/cluster/metadata.rb +142 -0
- data/lib/cassandra/cluster/options.rb +145 -0
- data/lib/cassandra/cluster/registry.rb +284 -0
- data/lib/cassandra/cluster/schema.rb +405 -0
- data/lib/cassandra/cluster/schema/cql_type_parser.rb +112 -0
- data/lib/cassandra/cluster/schema/fetchers.rb +1627 -0
- data/lib/cassandra/cluster/schema/fqcn_type_parser.rb +175 -0
- data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
- data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +45 -0
- data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
- data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
- data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +102 -0
- data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
- data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
- data/lib/cassandra/column.rb +66 -0
- data/lib/cassandra/column_container.rb +326 -0
- data/lib/cassandra/compression.rb +69 -0
- data/lib/cassandra/compression/compressors/lz4.rb +73 -0
- data/lib/cassandra/compression/compressors/snappy.rb +69 -0
- data/lib/cassandra/custom_data.rb +53 -0
- data/lib/cassandra/driver.rb +260 -0
- data/lib/cassandra/errors.rb +784 -0
- data/lib/cassandra/execution/info.rb +69 -0
- data/lib/cassandra/execution/options.rb +267 -0
- data/lib/cassandra/execution/profile.rb +153 -0
- data/lib/cassandra/execution/profile_manager.rb +71 -0
- data/lib/cassandra/execution/trace.rb +192 -0
- data/lib/cassandra/executors.rb +113 -0
- data/lib/cassandra/function.rb +156 -0
- data/lib/cassandra/function_collection.rb +85 -0
- data/lib/cassandra/future.rb +794 -0
- data/lib/cassandra/host.rb +102 -0
- data/lib/cassandra/index.rb +118 -0
- data/lib/cassandra/keyspace.rb +473 -0
- data/lib/cassandra/listener.rb +87 -0
- data/lib/cassandra/load_balancing.rb +121 -0
- data/lib/cassandra/load_balancing/policies.rb +20 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +172 -0
- data/lib/cassandra/load_balancing/policies/round_robin.rb +141 -0
- data/lib/cassandra/load_balancing/policies/token_aware.rb +149 -0
- data/lib/cassandra/load_balancing/policies/white_list.rb +100 -0
- data/lib/cassandra/materialized_view.rb +92 -0
- data/lib/cassandra/null_logger.rb +56 -0
- data/lib/cassandra/protocol.rb +102 -0
- data/lib/cassandra/protocol/coder.rb +1085 -0
- data/lib/cassandra/protocol/cql_byte_buffer.rb +418 -0
- data/lib/cassandra/protocol/cql_protocol_handler.rb +448 -0
- data/lib/cassandra/protocol/request.rb +41 -0
- data/lib/cassandra/protocol/requests/auth_response_request.rb +51 -0
- data/lib/cassandra/protocol/requests/batch_request.rb +117 -0
- data/lib/cassandra/protocol/requests/credentials_request.rb +51 -0
- data/lib/cassandra/protocol/requests/execute_request.rb +122 -0
- data/lib/cassandra/protocol/requests/options_request.rb +39 -0
- data/lib/cassandra/protocol/requests/prepare_request.rb +59 -0
- data/lib/cassandra/protocol/requests/query_request.rb +112 -0
- data/lib/cassandra/protocol/requests/register_request.rb +38 -0
- data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
- data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
- data/lib/cassandra/protocol/response.rb +28 -0
- data/lib/cassandra/protocol/responses/already_exists_error_response.rb +50 -0
- data/lib/cassandra/protocol/responses/auth_challenge_response.rb +36 -0
- data/lib/cassandra/protocol/responses/auth_success_response.rb +36 -0
- data/lib/cassandra/protocol/responses/authenticate_response.rb +36 -0
- data/lib/cassandra/protocol/responses/error_response.rb +142 -0
- data/lib/cassandra/protocol/responses/event_response.rb +30 -0
- data/lib/cassandra/protocol/responses/function_failure_error_response.rb +52 -0
- data/lib/cassandra/protocol/responses/prepared_result_response.rb +62 -0
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +59 -0
- data/lib/cassandra/protocol/responses/read_failure_error_response.rb +71 -0
- data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +61 -0
- data/lib/cassandra/protocol/responses/ready_response.rb +43 -0
- data/lib/cassandra/protocol/responses/result_response.rb +42 -0
- data/lib/cassandra/protocol/responses/rows_result_response.rb +39 -0
- data/lib/cassandra/protocol/responses/schema_change_event_response.rb +73 -0
- data/lib/cassandra/protocol/responses/schema_change_result_response.rb +70 -0
- data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +37 -0
- data/lib/cassandra/protocol/responses/status_change_event_response.rb +39 -0
- data/lib/cassandra/protocol/responses/supported_response.rb +36 -0
- data/lib/cassandra/protocol/responses/topology_change_event_response.rb +33 -0
- data/lib/cassandra/protocol/responses/unavailable_error_response.rb +58 -0
- data/lib/cassandra/protocol/responses/unprepared_error_response.rb +48 -0
- data/lib/cassandra/protocol/responses/void_result_response.rb +34 -0
- data/lib/cassandra/protocol/responses/write_failure_error_response.rb +73 -0
- data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +63 -0
- data/lib/cassandra/protocol/v1.rb +326 -0
- data/lib/cassandra/protocol/v3.rb +358 -0
- data/lib/cassandra/protocol/v4.rb +478 -0
- data/lib/cassandra/reconnection.rb +49 -0
- data/lib/cassandra/reconnection/policies.rb +20 -0
- data/lib/cassandra/reconnection/policies/constant.rb +46 -0
- data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
- data/lib/cassandra/result.rb +276 -0
- data/lib/cassandra/retry.rb +154 -0
- data/lib/cassandra/retry/policies.rb +21 -0
- data/lib/cassandra/retry/policies/default.rb +53 -0
- data/lib/cassandra/retry/policies/downgrading_consistency.rb +73 -0
- data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
- data/lib/cassandra/session.rb +270 -0
- data/lib/cassandra/statement.rb +32 -0
- data/lib/cassandra/statements.rb +23 -0
- data/lib/cassandra/statements/batch.rb +146 -0
- data/lib/cassandra/statements/bound.rb +65 -0
- data/lib/cassandra/statements/prepared.rb +235 -0
- data/lib/cassandra/statements/simple.rb +118 -0
- data/lib/cassandra/statements/void.rb +38 -0
- data/lib/cassandra/table.rb +240 -0
- data/lib/cassandra/time.rb +103 -0
- data/lib/cassandra/time_uuid.rb +78 -0
- data/lib/cassandra/timestamp_generator.rb +37 -0
- data/lib/cassandra/timestamp_generator/simple.rb +38 -0
- data/lib/cassandra/timestamp_generator/ticking_on_duplicate.rb +58 -0
- data/lib/cassandra/trigger.rb +67 -0
- data/lib/cassandra/tuple.rb +131 -0
- data/lib/cassandra/types.rb +1704 -0
- data/lib/cassandra/udt.rb +443 -0
- data/lib/cassandra/util.rb +464 -0
- data/lib/cassandra/uuid.rb +110 -0
- data/lib/cassandra/uuid/generator.rb +212 -0
- data/lib/cassandra/version.rb +21 -0
- data/lib/datastax/cassandra.rb +47 -0
- data/lib/ycql.rb +842 -0
- metadata +243 -0
|
@@ -0,0 +1,1627 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 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
|
+
# rubocop:disable Metrics/LineLength
|
|
30
|
+
Ione::Future.all(select_keyspaces(connection),
|
|
31
|
+
select_tables(connection),
|
|
32
|
+
select_columns(connection),
|
|
33
|
+
select_types(connection),
|
|
34
|
+
select_functions(connection),
|
|
35
|
+
select_aggregates(connection),
|
|
36
|
+
select_materialized_views(connection),
|
|
37
|
+
select_indexes(connection),
|
|
38
|
+
select_triggers(connection))
|
|
39
|
+
.map do |rows_keyspaces, rows_tables, rows_columns, rows_types, rows_functions, rows_aggregates, rows_views, rows_indexes, rows_triggers|
|
|
40
|
+
lookup_tables = map_rows_by(rows_tables, 'keyspace_name')
|
|
41
|
+
lookup_columns = map_rows_by(rows_columns, 'keyspace_name')
|
|
42
|
+
lookup_types = map_rows_by(rows_types, 'keyspace_name')
|
|
43
|
+
lookup_functions = map_rows_by(rows_functions, 'keyspace_name')
|
|
44
|
+
lookup_aggregates = map_rows_by(rows_aggregates, 'keyspace_name')
|
|
45
|
+
lookup_views = map_rows_by(rows_views, 'keyspace_name')
|
|
46
|
+
lookup_indexes = map_rows_by(rows_indexes, 'keyspace_name')
|
|
47
|
+
lookup_triggers = map_rows_by(rows_triggers, 'keyspace_name')
|
|
48
|
+
|
|
49
|
+
rows_keyspaces.map do |keyspace_data|
|
|
50
|
+
name = keyspace_data['keyspace_name']
|
|
51
|
+
|
|
52
|
+
create_keyspace(keyspace_data,
|
|
53
|
+
lookup_tables[name],
|
|
54
|
+
lookup_columns[name],
|
|
55
|
+
lookup_types[name],
|
|
56
|
+
lookup_functions[name],
|
|
57
|
+
lookup_aggregates[name],
|
|
58
|
+
lookup_views[name],
|
|
59
|
+
lookup_indexes[name],
|
|
60
|
+
lookup_triggers[name])
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def fetch_keyspace(connection, keyspace_name)
|
|
66
|
+
Ione::Future.all(select_keyspace(connection, keyspace_name),
|
|
67
|
+
select_keyspace_tables(connection, keyspace_name),
|
|
68
|
+
select_keyspace_columns(connection, keyspace_name),
|
|
69
|
+
select_keyspace_types(connection, keyspace_name),
|
|
70
|
+
select_keyspace_functions(connection, keyspace_name),
|
|
71
|
+
select_keyspace_aggregates(connection, keyspace_name),
|
|
72
|
+
select_keyspace_materialized_views(connection, keyspace_name),
|
|
73
|
+
select_keyspace_indexes(connection, keyspace_name),
|
|
74
|
+
select_keyspace_triggers(connection, keyspace_name))
|
|
75
|
+
.map do |rows_keyspaces, rows_tables, rows_columns, rows_types, rows_functions, rows_aggregates, rows_views, rows_indexes, rows_triggers|
|
|
76
|
+
if rows_keyspaces.empty?
|
|
77
|
+
nil
|
|
78
|
+
else
|
|
79
|
+
create_keyspace(rows_keyspaces.first,
|
|
80
|
+
rows_tables,
|
|
81
|
+
rows_columns,
|
|
82
|
+
rows_types,
|
|
83
|
+
rows_functions,
|
|
84
|
+
rows_aggregates,
|
|
85
|
+
rows_views,
|
|
86
|
+
rows_indexes,
|
|
87
|
+
rows_triggers)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def fetch_table(connection, keyspace_name, table_name)
|
|
93
|
+
Ione::Future.all(select_table(connection, keyspace_name, table_name),
|
|
94
|
+
select_table_columns(connection, keyspace_name, table_name),
|
|
95
|
+
select_table_indexes(connection, keyspace_name, table_name),
|
|
96
|
+
select_table_triggers(connection, keyspace_name, table_name))
|
|
97
|
+
.map do |(rows_tables, rows_columns, rows_indexes, rows_triggers)|
|
|
98
|
+
if rows_tables.empty?
|
|
99
|
+
nil
|
|
100
|
+
else
|
|
101
|
+
create_table(rows_tables.first,
|
|
102
|
+
rows_columns,
|
|
103
|
+
rows_indexes,
|
|
104
|
+
rows_triggers)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def fetch_materialized_view(connection, keyspace_name, view_name)
|
|
110
|
+
Ione::Future.all(select_materialized_view(connection, keyspace_name, view_name),
|
|
111
|
+
select_table_columns(connection, keyspace_name, view_name))
|
|
112
|
+
.map do |rows_views, rows_columns|
|
|
113
|
+
if rows_views.empty?
|
|
114
|
+
nil
|
|
115
|
+
else
|
|
116
|
+
view_row = rows_views.first
|
|
117
|
+
create_materialized_view(view_row,
|
|
118
|
+
rows_columns)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def fetch_type(connection, keyspace_name, type_name)
|
|
124
|
+
select_type(connection, keyspace_name, type_name).map do |rows_types|
|
|
125
|
+
if rows_types.empty?
|
|
126
|
+
nil
|
|
127
|
+
else
|
|
128
|
+
create_type(rows_types.first)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def fetch_function(connection, keyspace_name, function_name, function_args)
|
|
134
|
+
select_function(connection, keyspace_name, function_name, function_args)
|
|
135
|
+
.map do |rows_functions|
|
|
136
|
+
if rows_functions.empty?
|
|
137
|
+
nil
|
|
138
|
+
else
|
|
139
|
+
create_function(rows_functions.first)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def fetch_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
|
|
145
|
+
select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
|
|
146
|
+
.map do |rows_aggregates|
|
|
147
|
+
if rows_aggregates.empty?
|
|
148
|
+
nil
|
|
149
|
+
else
|
|
150
|
+
create_aggregate(rows_aggregates.first, @schema.keyspace(keyspace_name)
|
|
151
|
+
.send(:raw_functions))
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
private
|
|
157
|
+
|
|
158
|
+
def select_keyspaces(connection)
|
|
159
|
+
FUTURE_EMPTY_LIST
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def select_tables(connection)
|
|
163
|
+
FUTURE_EMPTY_LIST
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def select_materialized_views(connection)
|
|
167
|
+
FUTURE_EMPTY_LIST
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def select_columns(connection)
|
|
171
|
+
FUTURE_EMPTY_LIST
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def select_indexes(connection)
|
|
175
|
+
FUTURE_EMPTY_LIST
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def select_triggers(connection)
|
|
179
|
+
FUTURE_EMPTY_LIST
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def select_types(connection)
|
|
183
|
+
FUTURE_EMPTY_LIST
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def select_functions(connection)
|
|
187
|
+
FUTURE_EMPTY_LIST
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def select_aggregates(connection)
|
|
191
|
+
FUTURE_EMPTY_LIST
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def select_keyspace(connection, keyspace_name)
|
|
195
|
+
FUTURE_EMPTY_LIST
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def select_keyspace_tables(connection, keyspace_name)
|
|
199
|
+
FUTURE_EMPTY_LIST
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def select_keyspace_materialized_views(connection, keyspace_name)
|
|
203
|
+
FUTURE_EMPTY_LIST
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def select_keyspace_columns(connection, keyspace_name)
|
|
207
|
+
FUTURE_EMPTY_LIST
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def select_keyspace_indexes(connection, keyspace_name)
|
|
211
|
+
FUTURE_EMPTY_LIST
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def select_keyspace_triggers(connection, keyspace_name)
|
|
215
|
+
FUTURE_EMPTY_LIST
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def select_keyspace_types(connection, keyspace_name)
|
|
219
|
+
FUTURE_EMPTY_LIST
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def select_keyspace_functions(connection, keyspace_name)
|
|
223
|
+
FUTURE_EMPTY_LIST
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def select_keyspace_aggregates(connection, keyspace_name)
|
|
227
|
+
FUTURE_EMPTY_LIST
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def select_table(connection, keyspace_name, table_name)
|
|
231
|
+
FUTURE_EMPTY_LIST
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def select_materialized_view(connection, keyspace_name, view_name)
|
|
235
|
+
FUTURE_EMPTY_LIST
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def select_table_columns(connection, keyspace_name, table_name)
|
|
239
|
+
FUTURE_EMPTY_LIST
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def select_table_indexes(connection, keyspace_name, table_name)
|
|
243
|
+
FUTURE_EMPTY_LIST
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def select_table_triggers(connection, keyspace_name, table_name)
|
|
247
|
+
FUTURE_EMPTY_LIST
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def select_type(connection, keyspace_name, type_name)
|
|
251
|
+
FUTURE_EMPTY_LIST
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def select_function(connection, keyspace_name, function_name, function_args)
|
|
255
|
+
FUTURE_EMPTY_LIST
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
|
|
259
|
+
FUTURE_EMPTY_LIST
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def send_select_request(connection, cql, params = EMPTY_LIST, types = EMPTY_LIST)
|
|
263
|
+
backtrace = caller
|
|
264
|
+
connection.send_request(
|
|
265
|
+
Protocol::QueryRequest.new(cql, params, types, :quorum)
|
|
266
|
+
).map do |r|
|
|
267
|
+
case r
|
|
268
|
+
when Protocol::RowsResultResponse
|
|
269
|
+
r.rows
|
|
270
|
+
when Protocol::ErrorResponse
|
|
271
|
+
e = r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :quorum, 0)
|
|
272
|
+
e.set_backtrace(backtrace)
|
|
273
|
+
raise e
|
|
274
|
+
else
|
|
275
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}", caller
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def map_rows_by(rows, key_name, &block)
|
|
281
|
+
rows.each_with_object(::Hash.new { EMPTY_LIST }) do |row, map|
|
|
282
|
+
key = row[key_name]
|
|
283
|
+
map[key] = [] unless map.key?(key)
|
|
284
|
+
|
|
285
|
+
map[key] << if block
|
|
286
|
+
yield(row)
|
|
287
|
+
else
|
|
288
|
+
row
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# @private
|
|
295
|
+
module Fetchers
|
|
296
|
+
# rubocop:disable Naming/ClassAndModuleCamelCase
|
|
297
|
+
class V1_2_x
|
|
298
|
+
SELECT_KEYSPACES = 'SELECT * FROM system.schema_keyspaces'.freeze
|
|
299
|
+
SELECT_TABLES = 'SELECT * FROM system.schema_columnfamilies'.freeze
|
|
300
|
+
SELECT_COLUMNS = 'SELECT * FROM system.schema_columns'.freeze
|
|
301
|
+
SELECT_KEYSPACE =
|
|
302
|
+
'SELECT * ' \
|
|
303
|
+
'FROM system.schema_keyspaces ' \
|
|
304
|
+
'WHERE keyspace_name = \'%s\''.freeze
|
|
305
|
+
SELECT_KEYSPACE_TABLES =
|
|
306
|
+
'SELECT * ' \
|
|
307
|
+
'FROM system.schema_columnfamilies ' \
|
|
308
|
+
'WHERE keyspace_name = \'%s\''.freeze
|
|
309
|
+
SELECT_KEYSPACE_COLUMNS =
|
|
310
|
+
'SELECT * FROM system.schema_columns WHERE keyspace_name = \'%s\''.freeze
|
|
311
|
+
SELECT_TABLE =
|
|
312
|
+
'SELECT * ' \
|
|
313
|
+
'FROM system.schema_columnfamilies ' \
|
|
314
|
+
'WHERE keyspace_name = \'%s\' AND columnfamily_name = \'%s\''.freeze
|
|
315
|
+
SELECT_TABLE_COLUMNS =
|
|
316
|
+
'SELECT * ' \
|
|
317
|
+
'FROM system.schema_columns ' \
|
|
318
|
+
'WHERE keyspace_name = \'%s\' AND columnfamily_name = \'%s\''.freeze
|
|
319
|
+
|
|
320
|
+
include Cassandra::Cluster::Schema::Fetcher
|
|
321
|
+
|
|
322
|
+
def initialize(type_parser, schema)
|
|
323
|
+
@type_parser = type_parser
|
|
324
|
+
@schema = schema
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
private
|
|
328
|
+
|
|
329
|
+
def select_keyspaces(connection)
|
|
330
|
+
send_select_request(connection, SELECT_KEYSPACES)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def select_tables(connection)
|
|
334
|
+
send_select_request(connection, SELECT_TABLES)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def select_columns(connection)
|
|
338
|
+
send_select_request(connection, SELECT_COLUMNS)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def select_keyspace(connection, keyspace_name)
|
|
342
|
+
send_select_request(connection, SELECT_KEYSPACE % keyspace_name)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def select_keyspace_tables(connection, keyspace_name)
|
|
346
|
+
send_select_request(connection, format(SELECT_KEYSPACE_TABLES, keyspace_name))
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def select_keyspace_columns(connection, keyspace_name)
|
|
350
|
+
send_select_request(connection, format(SELECT_KEYSPACE_COLUMNS, keyspace_name))
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def select_table(connection, keyspace_name, table_name)
|
|
354
|
+
send_select_request(connection, format(SELECT_TABLE, keyspace_name, table_name))
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def select_table_columns(connection, keyspace_name, table_name)
|
|
358
|
+
send_select_request(connection,
|
|
359
|
+
format(SELECT_TABLE_COLUMNS, keyspace_name, table_name))
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def create_replication(keyspace_data)
|
|
363
|
+
klass = keyspace_data['strategy_class']
|
|
364
|
+
klass.slice!(REPLICATION_PACKAGE_PREFIX)
|
|
365
|
+
options = ::JSON.load(keyspace_data['strategy_options'])
|
|
366
|
+
Keyspace::Replication.new(klass, options)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def create_keyspace(keyspace_data, rows_tables, rows_columns,
|
|
370
|
+
rows_types, rows_functions, rows_aggregates,
|
|
371
|
+
rows_views, rows_indexes, rows_triggers)
|
|
372
|
+
keyspace_name = keyspace_data['keyspace_name']
|
|
373
|
+
replication = create_replication(keyspace_data)
|
|
374
|
+
types = rows_types.each_with_object({}) do |row, h|
|
|
375
|
+
h[row['type_name']] = create_type(row)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Create a FunctionCollection for the functions and aggregates.
|
|
379
|
+
functions = Cassandra::FunctionCollection.new
|
|
380
|
+
rows_functions.each do |row|
|
|
381
|
+
functions.add_or_update(create_function(row))
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
aggregates = Cassandra::FunctionCollection.new
|
|
385
|
+
rows_aggregates.each do |row|
|
|
386
|
+
aggregates.add_or_update(create_aggregate(row, functions))
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
lookup_columns = map_rows_by(rows_columns, 'columnfamily_name')
|
|
390
|
+
lookup_indexes = map_rows_by(rows_indexes, 'columnfamily_name')
|
|
391
|
+
lookup_triggers = map_rows_by(rows_triggers, 'columnfamily_name')
|
|
392
|
+
tables = rows_tables.each_with_object({}) do |row, h|
|
|
393
|
+
table_name = row['columnfamily_name']
|
|
394
|
+
h[table_name] = create_table(row,
|
|
395
|
+
lookup_columns[table_name],
|
|
396
|
+
lookup_indexes[table_name],
|
|
397
|
+
lookup_triggers[table_name])
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
Keyspace.new(keyspace_name,
|
|
401
|
+
keyspace_data['durable_writes'],
|
|
402
|
+
replication,
|
|
403
|
+
tables,
|
|
404
|
+
types,
|
|
405
|
+
functions,
|
|
406
|
+
aggregates,
|
|
407
|
+
{})
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def create_table(table_data, rows_columns, rows_indexes, rows_triggers)
|
|
411
|
+
keyspace_name = table_data['keyspace_name']
|
|
412
|
+
table_name = table_data['columnfamily_name']
|
|
413
|
+
key_validator = @type_parser.parse(table_data['key_validator'])
|
|
414
|
+
comparator = @type_parser.parse(table_data['comparator'])
|
|
415
|
+
column_aliases = ::JSON.load(table_data['column_aliases'])
|
|
416
|
+
|
|
417
|
+
if comparator.collections.nil?
|
|
418
|
+
is_compact = true
|
|
419
|
+
if !column_aliases.empty? || rows_columns.empty?
|
|
420
|
+
has_value = true
|
|
421
|
+
clustering_size = comparator.results.size
|
|
422
|
+
else
|
|
423
|
+
has_value = false
|
|
424
|
+
clustering_size = 0
|
|
425
|
+
end
|
|
426
|
+
else
|
|
427
|
+
size = comparator.results.size
|
|
428
|
+
if !comparator.collections.empty?
|
|
429
|
+
is_compact = false
|
|
430
|
+
has_value = false
|
|
431
|
+
clustering_size = size - 2
|
|
432
|
+
elsif column_aliases.size == size - 1 &&
|
|
433
|
+
comparator.results.last.first == Cassandra::Types.varchar
|
|
434
|
+
is_compact = false
|
|
435
|
+
has_value = false
|
|
436
|
+
clustering_size = size - 1
|
|
437
|
+
else
|
|
438
|
+
is_compact = true
|
|
439
|
+
has_value = (!column_aliases.empty? || rows_columns.empty?)
|
|
440
|
+
clustering_size = size
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# Separate out the partition-key, clustering-columns, and other-columns
|
|
445
|
+
partition_key = []
|
|
446
|
+
clustering_columns = []
|
|
447
|
+
clustering_order = []
|
|
448
|
+
other_columns = []
|
|
449
|
+
|
|
450
|
+
compaction_strategy = create_compaction_strategy(table_data)
|
|
451
|
+
table_options =
|
|
452
|
+
create_table_options(table_data, compaction_strategy, is_compact)
|
|
453
|
+
|
|
454
|
+
key_aliases = ::JSON.load(table_data['key_aliases'])
|
|
455
|
+
|
|
456
|
+
key_validator.results.each_with_index do |(type, order, is_frozen), i|
|
|
457
|
+
key_alias = key_aliases.fetch(i) { i.zero? ? 'key' : "key#{i + 1}" }
|
|
458
|
+
|
|
459
|
+
partition_key[i] = Column.new(key_alias, type, order, false, is_frozen)
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
clustering_size.times do |i|
|
|
463
|
+
column_alias = column_aliases.fetch(i) { "column#{i + 1}" }
|
|
464
|
+
type, order, is_frozen = comparator.results.fetch(i)
|
|
465
|
+
|
|
466
|
+
clustering_columns[i] =
|
|
467
|
+
Column.new(column_alias, type, order, false, is_frozen)
|
|
468
|
+
clustering_order[i] = order
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
if has_value
|
|
472
|
+
value_alias = table_data['value_alias']
|
|
473
|
+
value_alias ||= 'value'
|
|
474
|
+
|
|
475
|
+
unless value_alias.empty?
|
|
476
|
+
type, order, is_frozen =
|
|
477
|
+
@type_parser.parse(table_data['default_validator']).results.first
|
|
478
|
+
other_columns <<
|
|
479
|
+
Column.new(value_alias, type, order, false, is_frozen)
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
index_rows = []
|
|
484
|
+
rows_columns.each do |row|
|
|
485
|
+
column = create_column(row)
|
|
486
|
+
other_columns << column
|
|
487
|
+
|
|
488
|
+
# In C* 1.2.x, index info is in the column metadata; rows_indexes is [].
|
|
489
|
+
index_rows << [column, row] unless row['index_type'].nil?
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
table = Cassandra::Table.new(@schema.keyspace(keyspace_name),
|
|
493
|
+
table_name,
|
|
494
|
+
partition_key,
|
|
495
|
+
clustering_columns,
|
|
496
|
+
other_columns,
|
|
497
|
+
table_options,
|
|
498
|
+
clustering_order,
|
|
499
|
+
table_data['id'])
|
|
500
|
+
|
|
501
|
+
# Create Index objects and add them to the table.
|
|
502
|
+
index_rows.each do |column, row|
|
|
503
|
+
create_index(table, column, row)
|
|
504
|
+
end
|
|
505
|
+
table
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def create_index(table, column, row_column)
|
|
509
|
+
# Most of this logic was taken from the Java driver.
|
|
510
|
+
options = {}
|
|
511
|
+
# For some versions of C*, this field could have a literal string 'null' value.
|
|
512
|
+
if !row_column['index_options'].nil? && row_column['index_options'] != 'null' &&
|
|
513
|
+
!row_column['index_options'].empty?
|
|
514
|
+
options = ::JSON.load(row_column['index_options'])
|
|
515
|
+
end
|
|
516
|
+
column_name = Util.escape_name(column.name)
|
|
517
|
+
target = if options.key?('index_keys')
|
|
518
|
+
"keys(#{column_name})"
|
|
519
|
+
elsif options.key?('index_keys_and_values')
|
|
520
|
+
"entries(#{column_name})"
|
|
521
|
+
elsif column.frozen? && (column.type == Cassandra::Types::Set ||
|
|
522
|
+
column.type == Cassandra::Types::List ||
|
|
523
|
+
column.type == Cassandra::Types::Map)
|
|
524
|
+
"full(#{column_name})"
|
|
525
|
+
else
|
|
526
|
+
column_name
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
table.add_index(Cassandra::Index.new(table,
|
|
530
|
+
row_column['index_name'],
|
|
531
|
+
row_column['index_type'].downcase.to_sym,
|
|
532
|
+
target,
|
|
533
|
+
options))
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def create_column(column_data)
|
|
537
|
+
name = column_data['column_name']
|
|
538
|
+
is_static = (column_data['type'] == 'STATIC')
|
|
539
|
+
type, order, is_frozen =
|
|
540
|
+
@type_parser.parse(column_data['validator']).results.first
|
|
541
|
+
Column.new(name, type, order, is_static, is_frozen)
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def create_compaction_strategy(table_data)
|
|
545
|
+
klass = table_data['compaction_strategy_class']
|
|
546
|
+
klass.slice!('org.apache.cassandra.db.compaction.')
|
|
547
|
+
options = ::JSON.load(table_data['compaction_strategy_options'])
|
|
548
|
+
ColumnContainer::Compaction.new(klass, options)
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def create_table_options(table_data, compaction_strategy, is_compact)
|
|
552
|
+
compression_parameters = ::JSON.load(table_data['compression_parameters'])
|
|
553
|
+
if compression_parameters['sstable_compression']
|
|
554
|
+
compression_parameters['sstable_compression']
|
|
555
|
+
.slice!(COMPRESSION_PACKAGE_PREFIX)
|
|
556
|
+
end
|
|
557
|
+
Cassandra::ColumnContainer::Options.new(
|
|
558
|
+
table_data['comment'],
|
|
559
|
+
table_data['read_repair_chance'],
|
|
560
|
+
table_data['local_read_repair_chance'],
|
|
561
|
+
table_data['gc_grace_seconds'],
|
|
562
|
+
table_data['caching'],
|
|
563
|
+
table_data['bloom_filter_fp_chance'] || 0.01,
|
|
564
|
+
table_data['populate_io_cache_on_flush'],
|
|
565
|
+
table_data['memtable_flush_period_in_ms'],
|
|
566
|
+
table_data['default_time_to_live'],
|
|
567
|
+
nil,
|
|
568
|
+
nil,
|
|
569
|
+
table_data['replicate_on_write'],
|
|
570
|
+
nil,
|
|
571
|
+
nil,
|
|
572
|
+
compaction_strategy,
|
|
573
|
+
compression_parameters,
|
|
574
|
+
is_compact,
|
|
575
|
+
table_data['crc_check_chance'],
|
|
576
|
+
table_data['extensions'],
|
|
577
|
+
nil
|
|
578
|
+
)
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
class V2_0_x < V1_2_x
|
|
583
|
+
SELECT_TRIGGERS = 'SELECT * FROM system.schema_triggers'.freeze
|
|
584
|
+
|
|
585
|
+
SELECT_KEYSPACE =
|
|
586
|
+
'SELECT * FROM system.schema_keyspaces WHERE keyspace_name = ?'.freeze
|
|
587
|
+
SELECT_KEYSPACE_TABLES =
|
|
588
|
+
'SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ?'.freeze
|
|
589
|
+
SELECT_KEYSPACE_COLUMNS =
|
|
590
|
+
'SELECT * FROM system.schema_columns WHERE keyspace_name = ?'.freeze
|
|
591
|
+
SELECT_KEYSPACE_TRIGGERS =
|
|
592
|
+
'SELECT * FROM system.schema_triggers WHERE keyspace_name = ?'.freeze
|
|
593
|
+
|
|
594
|
+
SELECT_TABLE =
|
|
595
|
+
'SELECT * ' \
|
|
596
|
+
'FROM system.schema_columnfamilies ' \
|
|
597
|
+
'WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
|
|
598
|
+
SELECT_TABLE_COLUMNS =
|
|
599
|
+
'SELECT * ' \
|
|
600
|
+
'FROM system.schema_columns ' \
|
|
601
|
+
'WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
|
|
602
|
+
SELECT_TABLE_TRIGGERS =
|
|
603
|
+
'SELECT * ' \
|
|
604
|
+
'FROM system.schema_triggers ' \
|
|
605
|
+
'WHERE keyspace_name = ? AND columnfamily_name = ?'.freeze
|
|
606
|
+
|
|
607
|
+
private
|
|
608
|
+
|
|
609
|
+
def create_table(table_data, rows_columns, rows_indexes, rows_triggers)
|
|
610
|
+
keyspace_name = table_data['keyspace_name']
|
|
611
|
+
table_name = table_data['columnfamily_name']
|
|
612
|
+
comparator = @type_parser.parse(table_data['comparator'])
|
|
613
|
+
clustering_size = 0
|
|
614
|
+
|
|
615
|
+
# Separate out partition-key, clustering columns, other columns.
|
|
616
|
+
partition_key = []
|
|
617
|
+
clustering_columns = []
|
|
618
|
+
clustering_order = []
|
|
619
|
+
other_columns = []
|
|
620
|
+
|
|
621
|
+
index_rows = []
|
|
622
|
+
rows_columns.each do |row|
|
|
623
|
+
next if row['column_name'].empty?
|
|
624
|
+
|
|
625
|
+
column = create_column(row)
|
|
626
|
+
type = row['type'].to_s
|
|
627
|
+
ind = row['component_index'] || 0
|
|
628
|
+
|
|
629
|
+
case type.upcase
|
|
630
|
+
when 'PARTITION_KEY'
|
|
631
|
+
partition_key[ind] = column
|
|
632
|
+
when 'CLUSTERING_KEY'
|
|
633
|
+
clustering_columns[ind] = column
|
|
634
|
+
clustering_order[ind] = column.order
|
|
635
|
+
|
|
636
|
+
clustering_size += 1
|
|
637
|
+
else
|
|
638
|
+
other_columns << column
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# In C* 2.0.x, index info is in the column metadata; rows_indexes is nil.
|
|
642
|
+
index_rows << [column, row] unless row['index_type'].nil?
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
compaction_strategy = create_compaction_strategy(table_data)
|
|
646
|
+
is_compact = (clustering_size != comparator.results.size - 1) ||
|
|
647
|
+
!comparator.collections
|
|
648
|
+
table_options =
|
|
649
|
+
create_table_options(table_data, compaction_strategy, is_compact)
|
|
650
|
+
|
|
651
|
+
table = Cassandra::Table.new(@schema.keyspace(keyspace_name),
|
|
652
|
+
table_name,
|
|
653
|
+
partition_key,
|
|
654
|
+
clustering_columns,
|
|
655
|
+
other_columns,
|
|
656
|
+
table_options,
|
|
657
|
+
clustering_order,
|
|
658
|
+
table_data['id'])
|
|
659
|
+
|
|
660
|
+
# Create Index objects and add them to the table.
|
|
661
|
+
index_rows.each do |column, row|
|
|
662
|
+
create_index(table, column, row)
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
# Create Trigger objects and add them to the table.
|
|
666
|
+
rows_triggers.each do |row_trigger|
|
|
667
|
+
table.add_trigger(Cassandra::Trigger.new(table,
|
|
668
|
+
row_trigger['trigger_name'],
|
|
669
|
+
row_trigger['trigger_options']))
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
table
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
def select_triggers(connection)
|
|
676
|
+
send_select_request(connection, SELECT_TRIGGERS)
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
def select_keyspace(connection, keyspace_name)
|
|
680
|
+
params = [keyspace_name]
|
|
681
|
+
hints = [Types.varchar]
|
|
682
|
+
send_select_request(connection, SELECT_KEYSPACE, params, hints)
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
def select_keyspace_tables(connection, keyspace_name)
|
|
686
|
+
params = [keyspace_name]
|
|
687
|
+
hints = [Types.varchar]
|
|
688
|
+
send_select_request(connection, SELECT_KEYSPACE_TABLES, params, hints)
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
def select_keyspace_columns(connection, keyspace_name)
|
|
692
|
+
params = [keyspace_name]
|
|
693
|
+
hints = [Types.varchar]
|
|
694
|
+
send_select_request(connection, SELECT_KEYSPACE_COLUMNS, params, hints)
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def select_keyspace_triggers(connection, keyspace_name)
|
|
698
|
+
params = [keyspace_name]
|
|
699
|
+
hints = [Types.varchar]
|
|
700
|
+
send_select_request(connection, SELECT_KEYSPACE_TRIGGERS, params, hints)
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
def select_table(connection, keyspace_name, table_name)
|
|
704
|
+
params = [keyspace_name, table_name]
|
|
705
|
+
hints = [Types.varchar, Types.varchar]
|
|
706
|
+
send_select_request(connection, SELECT_TABLE, params, hints)
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
def select_table_columns(connection, keyspace_name, table_name)
|
|
710
|
+
params = [keyspace_name, table_name]
|
|
711
|
+
hints = [Types.varchar, Types.varchar]
|
|
712
|
+
send_select_request(connection, SELECT_TABLE_COLUMNS, params, hints)
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
def select_table_triggers(connection, keyspace_name, table_name)
|
|
716
|
+
params = [keyspace_name, table_name]
|
|
717
|
+
hints = [Types.varchar, Types.varchar]
|
|
718
|
+
send_select_request(connection, SELECT_TABLE_TRIGGERS, params, hints)
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
def create_table_options(table_data, compaction_strategy, is_compact)
|
|
722
|
+
compression_parameters = ::JSON.load(table_data['compression_parameters'])
|
|
723
|
+
if compression_parameters['sstable_compression']
|
|
724
|
+
compression_parameters['sstable_compression']
|
|
725
|
+
.slice!(COMPRESSION_PACKAGE_PREFIX)
|
|
726
|
+
end
|
|
727
|
+
Cassandra::ColumnContainer::Options.new(
|
|
728
|
+
table_data['comment'],
|
|
729
|
+
table_data['read_repair_chance'],
|
|
730
|
+
table_data['local_read_repair_chance'],
|
|
731
|
+
table_data['gc_grace_seconds'],
|
|
732
|
+
table_data['caching'],
|
|
733
|
+
table_data['bloom_filter_fp_chance'],
|
|
734
|
+
table_data['populate_io_cache_on_flush'],
|
|
735
|
+
table_data['memtable_flush_period_in_ms'],
|
|
736
|
+
table_data['default_time_to_live'],
|
|
737
|
+
table_data['speculative_retry'],
|
|
738
|
+
table_data['index_interval'],
|
|
739
|
+
table_data['replicate_on_write'],
|
|
740
|
+
nil,
|
|
741
|
+
nil,
|
|
742
|
+
compaction_strategy,
|
|
743
|
+
compression_parameters,
|
|
744
|
+
is_compact,
|
|
745
|
+
table_data['crc_check_chance'],
|
|
746
|
+
table_data['extensions'],
|
|
747
|
+
nil
|
|
748
|
+
)
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
class V2_1_x < V2_0_x
|
|
753
|
+
SELECT_TYPES = 'SELECT * FROM system.schema_usertypes'.freeze
|
|
754
|
+
SELECT_KEYSPACE_TYPES =
|
|
755
|
+
'SELECT * FROM system.schema_usertypes WHERE keyspace_name = ?'.freeze
|
|
756
|
+
SELECT_TYPE =
|
|
757
|
+
'SELECT * ' \
|
|
758
|
+
'FROM system.schema_usertypes ' \
|
|
759
|
+
'WHERE keyspace_name = ? AND type_name = ?'.freeze
|
|
760
|
+
|
|
761
|
+
private
|
|
762
|
+
|
|
763
|
+
def create_type(type_data)
|
|
764
|
+
keyspace_name = type_data['keyspace_name']
|
|
765
|
+
type_name = type_data['type_name']
|
|
766
|
+
type_fields = ::Array.new
|
|
767
|
+
|
|
768
|
+
field_names = type_data['field_names']
|
|
769
|
+
field_types = type_data['field_types']
|
|
770
|
+
|
|
771
|
+
field_names.zip(field_types) do |(field_name, fqcn)|
|
|
772
|
+
field_type = @type_parser.parse(fqcn).results.first.first
|
|
773
|
+
|
|
774
|
+
type_fields << [field_name, field_type]
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
Types.udt(keyspace_name, type_name, type_fields)
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def select_types(connection)
|
|
781
|
+
send_select_request(connection, SELECT_TYPES)
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
def select_keyspace_types(connection, keyspace_name)
|
|
785
|
+
params = [keyspace_name]
|
|
786
|
+
hints = [Types.varchar]
|
|
787
|
+
send_select_request(connection, SELECT_KEYSPACE_TYPES, params, hints)
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
def select_type(connection, keyspace_name, type_name)
|
|
791
|
+
params = [keyspace_name, type_name]
|
|
792
|
+
hints = [Types.varchar, Types.varchar]
|
|
793
|
+
send_select_request(connection, SELECT_TYPE, params, hints)
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
def create_table_options(table_data, compaction_strategy, is_compact)
|
|
797
|
+
compression_parameters = ::JSON.load(table_data['compression_parameters'])
|
|
798
|
+
if compression_parameters['sstable_compression']
|
|
799
|
+
compression_parameters['sstable_compression']
|
|
800
|
+
.slice!(COMPRESSION_PACKAGE_PREFIX)
|
|
801
|
+
end
|
|
802
|
+
Cassandra::ColumnContainer::Options.new(
|
|
803
|
+
table_data['comment'],
|
|
804
|
+
table_data['read_repair_chance'],
|
|
805
|
+
table_data['local_read_repair_chance'],
|
|
806
|
+
table_data['gc_grace_seconds'],
|
|
807
|
+
table_data['caching'],
|
|
808
|
+
table_data['bloom_filter_fp_chance'],
|
|
809
|
+
table_data['populate_io_cache_on_flush'],
|
|
810
|
+
table_data['memtable_flush_period_in_ms'],
|
|
811
|
+
table_data['default_time_to_live'],
|
|
812
|
+
table_data['speculative_retry'],
|
|
813
|
+
table_data['index_interval'],
|
|
814
|
+
table_data['replicate_on_write'],
|
|
815
|
+
table_data['min_index_interval'],
|
|
816
|
+
table_data['max_index_interval'],
|
|
817
|
+
compaction_strategy,
|
|
818
|
+
compression_parameters,
|
|
819
|
+
is_compact,
|
|
820
|
+
table_data['crc_check_chance'],
|
|
821
|
+
table_data['extensions'],
|
|
822
|
+
nil
|
|
823
|
+
)
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
class V2_2_x < V2_1_x
|
|
828
|
+
SELECT_FUNCTIONS = 'SELECT * FROM system.schema_functions'.freeze
|
|
829
|
+
SELECT_AGGREGATES = 'SELECT * FROM system.schema_aggregates'.freeze
|
|
830
|
+
SELECT_KEYSPACE_FUNCTIONS =
|
|
831
|
+
'SELECT * FROM system.schema_functions WHERE keyspace_name = ?'.freeze
|
|
832
|
+
SELECT_KEYSPACE_AGGREGATES =
|
|
833
|
+
'SELECT * FROM system.schema_aggregates WHERE keyspace_name = ?'.freeze
|
|
834
|
+
SELECT_FUNCTION =
|
|
835
|
+
'SELECT * ' \
|
|
836
|
+
'FROM system.schema_functions ' \
|
|
837
|
+
'WHERE keyspace_name = ? AND function_name = ? ' \
|
|
838
|
+
'AND argument_types = ?'.freeze
|
|
839
|
+
SELECT_AGGREGATE =
|
|
840
|
+
'SELECT * ' \
|
|
841
|
+
'FROM system.schema_aggregates ' \
|
|
842
|
+
'WHERE keyspace_name = ? AND aggregate_name = ? ' \
|
|
843
|
+
'AND argument_types = ?'.freeze
|
|
844
|
+
|
|
845
|
+
# parse an array of string argument types and return an array of
|
|
846
|
+
# [Cassandra::Type]s.
|
|
847
|
+
# @param connection a connection to a Cassandra node.
|
|
848
|
+
# @param keyspace_name [String] name of the keyspace.
|
|
849
|
+
# @param argument_types [Array<String>] array of argument types.
|
|
850
|
+
# @return [Array<Cassandra::Type>] array of parsed types.
|
|
851
|
+
def parse_argument_types(connection, keyspace_name, argument_types)
|
|
852
|
+
argument_types.map do |argument_type|
|
|
853
|
+
@type_parser.parse(argument_type).results.first.first
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
private
|
|
858
|
+
|
|
859
|
+
def create_function(function_data)
|
|
860
|
+
keyspace_name = function_data['keyspace_name']
|
|
861
|
+
function_name = function_data['function_name']
|
|
862
|
+
function_lang = function_data['language']
|
|
863
|
+
function_type =
|
|
864
|
+
@type_parser.parse(function_data['return_type']).results.first.first
|
|
865
|
+
function_body = function_data['body']
|
|
866
|
+
called_on_null = function_data['called_on_null_input']
|
|
867
|
+
|
|
868
|
+
arguments = []
|
|
869
|
+
|
|
870
|
+
Array(function_data['argument_names'])
|
|
871
|
+
.zip(Array(function_data['argument_types'])) do |argument_name, fqcn|
|
|
872
|
+
argument_type = @type_parser.parse(fqcn).results.first.first
|
|
873
|
+
arguments << Argument.new(argument_name, argument_type)
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
Cassandra::Function.new(keyspace_name,
|
|
877
|
+
function_name,
|
|
878
|
+
function_lang,
|
|
879
|
+
function_type,
|
|
880
|
+
arguments,
|
|
881
|
+
function_body,
|
|
882
|
+
called_on_null)
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def create_aggregate(aggregate_data, functions)
|
|
886
|
+
keyspace_name = aggregate_data['keyspace_name']
|
|
887
|
+
aggregate_name = aggregate_data['aggregate_name']
|
|
888
|
+
aggregate_type =
|
|
889
|
+
@type_parser.parse(aggregate_data['return_type']).results.first.first
|
|
890
|
+
argument_types = aggregate_data['argument_types'].map do |fqcn|
|
|
891
|
+
@type_parser.parse(fqcn).results.first.first
|
|
892
|
+
end.freeze
|
|
893
|
+
state_type =
|
|
894
|
+
@type_parser.parse(aggregate_data['state_type']).results.first.first
|
|
895
|
+
initial_state = Util.encode_object(
|
|
896
|
+
Protocol::Coder.read_value_v4(
|
|
897
|
+
Protocol::CqlByteBuffer.new.append_bytes(aggregate_data['initcond']),
|
|
898
|
+
state_type, nil
|
|
899
|
+
)
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
# The state-function takes arguments: first the stype, then the args of the aggregate.
|
|
903
|
+
state_function = functions.get(aggregate_data['state_func'],
|
|
904
|
+
[state_type].concat(argument_types))
|
|
905
|
+
|
|
906
|
+
# The final-function takes an stype argument.
|
|
907
|
+
final_function = functions.get(aggregate_data['final_func'],
|
|
908
|
+
[state_type])
|
|
909
|
+
|
|
910
|
+
Aggregate.new(keyspace_name,
|
|
911
|
+
aggregate_name,
|
|
912
|
+
aggregate_type,
|
|
913
|
+
argument_types,
|
|
914
|
+
state_type,
|
|
915
|
+
initial_state,
|
|
916
|
+
state_function,
|
|
917
|
+
final_function)
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
def select_functions(connection)
|
|
921
|
+
send_select_request(connection, SELECT_FUNCTIONS)
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
def select_aggregates(connection)
|
|
925
|
+
send_select_request(connection, SELECT_AGGREGATES)
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
def select_keyspace_functions(connection, keyspace_name)
|
|
929
|
+
params = [keyspace_name]
|
|
930
|
+
hints = [Types.varchar]
|
|
931
|
+
send_select_request(connection, SELECT_KEYSPACE_FUNCTIONS, params, hints)
|
|
932
|
+
end
|
|
933
|
+
|
|
934
|
+
def select_keyspace_aggregates(connection, keyspace_name)
|
|
935
|
+
params = [keyspace_name]
|
|
936
|
+
hints = [Types.varchar]
|
|
937
|
+
send_select_request(connection, SELECT_KEYSPACE_AGGREGATES, params, hints)
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
def select_function(connection, keyspace_name, function_name, function_args)
|
|
941
|
+
params = [keyspace_name, function_name, function_args.map(&:to_s)]
|
|
942
|
+
hints = [Types.varchar, Types.varchar, Types.list(Types.varchar)]
|
|
943
|
+
send_select_request(connection, SELECT_FUNCTION, params, hints)
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
def select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
|
|
947
|
+
params = [keyspace_name, aggregate_name, aggregate_args.map(&:to_s)]
|
|
948
|
+
hints = [Types.varchar, Types.varchar, Types.list(Types.varchar)]
|
|
949
|
+
send_select_request(connection, SELECT_AGGREGATE, params, hints)
|
|
950
|
+
end
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
class V3_0_x < V2_2_x
|
|
954
|
+
SELECT_KEYSPACES = 'SELECT * FROM system_schema.keyspaces'.freeze
|
|
955
|
+
SELECT_TABLES = 'SELECT * FROM system_schema.tables'.freeze
|
|
956
|
+
SELECT_COLUMNS = 'SELECT * FROM system_schema.columns'.freeze
|
|
957
|
+
SELECT_TYPES = 'SELECT * FROM system_schema.types'.freeze
|
|
958
|
+
SELECT_FUNCTIONS = 'SELECT * FROM system_schema.functions'.freeze
|
|
959
|
+
SELECT_AGGREGATES = 'SELECT * FROM system_schema.aggregates'.freeze
|
|
960
|
+
SELECT_INDEXES = 'SELECT * FROM system_schema.indexes'.freeze
|
|
961
|
+
SELECT_VIEWS = 'SELECT * FROM system_schema.views'.freeze
|
|
962
|
+
SELECT_TRIGGERS = 'SELECT * FROM system_schema.triggers'.freeze
|
|
963
|
+
|
|
964
|
+
SELECT_KEYSPACE =
|
|
965
|
+
'SELECT * FROM system_schema.keyspaces WHERE keyspace_name = ?'.freeze
|
|
966
|
+
SELECT_KEYSPACE_TABLES =
|
|
967
|
+
'SELECT * FROM system_schema.tables WHERE keyspace_name = ?'.freeze
|
|
968
|
+
SELECT_KEYSPACE_INDEXES =
|
|
969
|
+
'SELECT * FROM system_schema.indexes WHERE keyspace_name = ?'.freeze
|
|
970
|
+
SELECT_KEYSPACE_COLUMNS =
|
|
971
|
+
'SELECT * FROM system_schema.columns WHERE keyspace_name = ?'.freeze
|
|
972
|
+
SELECT_KEYSPACE_VIEWS =
|
|
973
|
+
'SELECT * FROM system_schema.views WHERE keyspace_name = ?'.freeze
|
|
974
|
+
SELECT_KEYSPACE_TYPES =
|
|
975
|
+
'SELECT * FROM system_schema.types WHERE keyspace_name = ?'.freeze
|
|
976
|
+
SELECT_KEYSPACE_FUNCTIONS =
|
|
977
|
+
'SELECT * FROM system_schema.functions WHERE keyspace_name = ?'.freeze
|
|
978
|
+
SELECT_KEYSPACE_AGGREGATES =
|
|
979
|
+
'SELECT * FROM system_schema.aggregates WHERE keyspace_name = ?'.freeze
|
|
980
|
+
SELECT_KEYSPACE_TRIGGERS =
|
|
981
|
+
'SELECT * FROM system_schema.triggers WHERE keyspace_name = ?'.freeze
|
|
982
|
+
|
|
983
|
+
SELECT_TABLE =
|
|
984
|
+
'SELECT * ' \
|
|
985
|
+
'FROM system_schema.tables ' \
|
|
986
|
+
'WHERE keyspace_name = ? AND table_name = ?'.freeze
|
|
987
|
+
SELECT_TABLE_COLUMNS =
|
|
988
|
+
'SELECT * ' \
|
|
989
|
+
'FROM system_schema.columns ' \
|
|
990
|
+
'WHERE keyspace_name = ? AND table_name = ?'.freeze
|
|
991
|
+
SELECT_TABLE_INDEXES =
|
|
992
|
+
'SELECT * ' \
|
|
993
|
+
'FROM system_schema.indexes ' \
|
|
994
|
+
'WHERE keyspace_name = ? AND table_name = ?'.freeze
|
|
995
|
+
SELECT_TABLE_TRIGGERS =
|
|
996
|
+
'SELECT * ' \
|
|
997
|
+
'FROM system_schema.triggers ' \
|
|
998
|
+
'WHERE keyspace_name = ? AND table_name = ?'.freeze
|
|
999
|
+
|
|
1000
|
+
SELECT_VIEW =
|
|
1001
|
+
'SELECT * ' \
|
|
1002
|
+
'FROM system_schema.views ' \
|
|
1003
|
+
'WHERE keyspace_name = ? AND view_name = ?'.freeze
|
|
1004
|
+
|
|
1005
|
+
SELECT_TYPE =
|
|
1006
|
+
'SELECT * ' \
|
|
1007
|
+
'FROM system_schema.types ' \
|
|
1008
|
+
'WHERE keyspace_name = ? AND type_name = ?'.freeze
|
|
1009
|
+
|
|
1010
|
+
SELECT_FUNCTION =
|
|
1011
|
+
'SELECT * ' \
|
|
1012
|
+
'FROM system_schema.functions ' \
|
|
1013
|
+
'WHERE keyspace_name = ? AND function_name = ? ' \
|
|
1014
|
+
'AND argument_types = ?'.freeze
|
|
1015
|
+
|
|
1016
|
+
SELECT_AGGREGATE =
|
|
1017
|
+
'SELECT * ' \
|
|
1018
|
+
'FROM system_schema.aggregates ' \
|
|
1019
|
+
'WHERE keyspace_name = ? AND aggregate_name = ? ' \
|
|
1020
|
+
'AND argument_types = ?'.freeze
|
|
1021
|
+
|
|
1022
|
+
# parse an array of string argument types and return an array of
|
|
1023
|
+
# [Cassandra::Type]s.
|
|
1024
|
+
# @param connection a connection to a Cassandra node.
|
|
1025
|
+
# @param keyspace_name [String] name of the keyspace.
|
|
1026
|
+
# @param argument_types [Array<String>] array of argument types.
|
|
1027
|
+
# @return [Array<Cassandra::Type>] array of parsed types.
|
|
1028
|
+
def parse_argument_types(connection, keyspace_name, argument_types)
|
|
1029
|
+
types = @schema.keyspace(keyspace_name).send(:raw_types)
|
|
1030
|
+
argument_types.map do |argument_type|
|
|
1031
|
+
@type_parser.parse(argument_type, types).first
|
|
1032
|
+
end
|
|
1033
|
+
end
|
|
1034
|
+
|
|
1035
|
+
private
|
|
1036
|
+
|
|
1037
|
+
def select_keyspaces(connection)
|
|
1038
|
+
send_select_request(connection, SELECT_KEYSPACES)
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
def select_tables(connection)
|
|
1042
|
+
send_select_request(connection, SELECT_TABLES)
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
def select_indexes(connection)
|
|
1046
|
+
send_select_request(connection, SELECT_INDEXES)
|
|
1047
|
+
end
|
|
1048
|
+
|
|
1049
|
+
def select_materialized_views(connection)
|
|
1050
|
+
send_select_request(connection, SELECT_VIEWS)
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
def select_columns(connection)
|
|
1054
|
+
send_select_request(connection, SELECT_COLUMNS)
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
def select_triggers(connection)
|
|
1058
|
+
send_select_request(connection, SELECT_TRIGGERS)
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
def select_types(connection)
|
|
1062
|
+
send_select_request(connection, SELECT_TYPES)
|
|
1063
|
+
end
|
|
1064
|
+
|
|
1065
|
+
def select_functions(connection)
|
|
1066
|
+
send_select_request(connection, SELECT_FUNCTIONS)
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
def select_aggregates(connection)
|
|
1070
|
+
send_select_request(connection, SELECT_AGGREGATES)
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
def select_keyspace(connection, keyspace_name)
|
|
1074
|
+
params = [keyspace_name]
|
|
1075
|
+
hints = [Types.varchar]
|
|
1076
|
+
send_select_request(connection, SELECT_KEYSPACE, params, hints)
|
|
1077
|
+
end
|
|
1078
|
+
|
|
1079
|
+
def select_keyspace_tables(connection, keyspace_name)
|
|
1080
|
+
params = [keyspace_name]
|
|
1081
|
+
hints = [Types.varchar]
|
|
1082
|
+
send_select_request(connection, SELECT_KEYSPACE_TABLES, params, hints)
|
|
1083
|
+
end
|
|
1084
|
+
|
|
1085
|
+
def select_keyspace_columns(connection, keyspace_name)
|
|
1086
|
+
params = [keyspace_name]
|
|
1087
|
+
hints = [Types.varchar]
|
|
1088
|
+
send_select_request(connection, SELECT_KEYSPACE_COLUMNS, params, hints)
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1091
|
+
def select_keyspace_indexes(connection, keyspace_name)
|
|
1092
|
+
params = [keyspace_name]
|
|
1093
|
+
hints = [Types.varchar]
|
|
1094
|
+
send_select_request(connection, SELECT_KEYSPACE_INDEXES, params, hints)
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
def select_keyspace_materialized_views(connection, keyspace_name)
|
|
1098
|
+
params = [keyspace_name]
|
|
1099
|
+
hints = [Types.varchar]
|
|
1100
|
+
send_select_request(connection, SELECT_KEYSPACE_VIEWS, params, hints)
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
def select_keyspace_triggers(connection, keyspace_name)
|
|
1104
|
+
params = [keyspace_name]
|
|
1105
|
+
hints = [Types.varchar]
|
|
1106
|
+
send_select_request(connection, SELECT_KEYSPACE_TRIGGERS, params, hints)
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1109
|
+
def select_keyspace_types(connection, keyspace_name)
|
|
1110
|
+
params = [keyspace_name]
|
|
1111
|
+
hints = [Types.varchar]
|
|
1112
|
+
send_select_request(connection, SELECT_KEYSPACE_TYPES, params, hints)
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
def select_keyspace_functions(connection, keyspace_name)
|
|
1116
|
+
params = [keyspace_name]
|
|
1117
|
+
hints = [Types.varchar]
|
|
1118
|
+
send_select_request(connection, SELECT_KEYSPACE_FUNCTIONS, params, hints)
|
|
1119
|
+
end
|
|
1120
|
+
|
|
1121
|
+
def select_keyspace_aggregates(connection, keyspace_name)
|
|
1122
|
+
params = [keyspace_name]
|
|
1123
|
+
hints = [Types.varchar]
|
|
1124
|
+
send_select_request(connection, SELECT_KEYSPACE_AGGREGATES, params, hints)
|
|
1125
|
+
end
|
|
1126
|
+
|
|
1127
|
+
def select_table(connection, keyspace_name, table_name)
|
|
1128
|
+
params = [keyspace_name, table_name]
|
|
1129
|
+
hints = [Types.varchar, Types.varchar]
|
|
1130
|
+
send_select_request(connection, SELECT_TABLE, params, hints)
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
def select_table_columns(connection, keyspace_name, table_name)
|
|
1134
|
+
# This is identical to the 2.0 impl, but the SELECT_TABLE_COLUMNS query
|
|
1135
|
+
# is different between the two, so we need two impls. :(
|
|
1136
|
+
# Also, this method works fine for finding view columns as well.
|
|
1137
|
+
params = [keyspace_name, table_name]
|
|
1138
|
+
hints = [Types.varchar, Types.varchar]
|
|
1139
|
+
send_select_request(connection, SELECT_TABLE_COLUMNS, params, hints)
|
|
1140
|
+
end
|
|
1141
|
+
|
|
1142
|
+
def select_table_indexes(connection, keyspace_name, table_name)
|
|
1143
|
+
params = [keyspace_name, table_name]
|
|
1144
|
+
hints = [Types.varchar, Types.varchar]
|
|
1145
|
+
send_select_request(connection, SELECT_TABLE_INDEXES, params, hints)
|
|
1146
|
+
end
|
|
1147
|
+
|
|
1148
|
+
def select_materialized_view(connection, keyspace_name, view_name)
|
|
1149
|
+
params = [keyspace_name, view_name]
|
|
1150
|
+
hints = [Types.varchar, Types.varchar]
|
|
1151
|
+
send_select_request(connection, SELECT_VIEW, params, hints)
|
|
1152
|
+
end
|
|
1153
|
+
|
|
1154
|
+
def select_table_triggers(connection, keyspace_name, table_name)
|
|
1155
|
+
params = [keyspace_name, table_name]
|
|
1156
|
+
hints = [Types.varchar, Types.varchar]
|
|
1157
|
+
send_select_request(connection, SELECT_TABLE_TRIGGERS, params, hints)
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
def select_type(connection, keyspace_name, type_name)
|
|
1161
|
+
params = [keyspace_name, type_name]
|
|
1162
|
+
hints = [Types.varchar, Types.varchar]
|
|
1163
|
+
send_select_request(connection, SELECT_TYPE, params, hints)
|
|
1164
|
+
end
|
|
1165
|
+
|
|
1166
|
+
def select_function(connection, keyspace_name, function_name, function_args)
|
|
1167
|
+
params = [keyspace_name, function_name, function_args.map(&:to_s)]
|
|
1168
|
+
hints = [Types.varchar, Types.varchar, Types.list(Types.varchar)]
|
|
1169
|
+
send_select_request(connection, SELECT_FUNCTION, params, hints)
|
|
1170
|
+
end
|
|
1171
|
+
|
|
1172
|
+
def select_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
|
|
1173
|
+
params = [keyspace_name, aggregate_name, aggregate_args.map(&:to_s)]
|
|
1174
|
+
hints = [Types.varchar, Types.varchar, Types.list(Types.varchar)]
|
|
1175
|
+
send_select_request(connection, SELECT_AGGREGATE, params, hints)
|
|
1176
|
+
end
|
|
1177
|
+
|
|
1178
|
+
def create_function(function_data, types = nil)
|
|
1179
|
+
keyspace_name = function_data['keyspace_name']
|
|
1180
|
+
function_name = function_data['function_name']
|
|
1181
|
+
function_lang = function_data['language']
|
|
1182
|
+
types ||= @schema.keyspace(keyspace_name).send(:raw_types)
|
|
1183
|
+
function_type = @type_parser.parse(function_data['return_type'], types).first
|
|
1184
|
+
function_body = function_data['body']
|
|
1185
|
+
called_on_null = function_data['called_on_null_input']
|
|
1186
|
+
|
|
1187
|
+
arguments = []
|
|
1188
|
+
|
|
1189
|
+
function_data['argument_names']
|
|
1190
|
+
.zip(function_data['argument_types']) do |argument_name, argument_type|
|
|
1191
|
+
arguments << Argument.new(argument_name,
|
|
1192
|
+
@type_parser.parse(argument_type, types).first)
|
|
1193
|
+
end
|
|
1194
|
+
|
|
1195
|
+
Cassandra::Function.new(keyspace_name,
|
|
1196
|
+
function_name,
|
|
1197
|
+
function_lang,
|
|
1198
|
+
function_type,
|
|
1199
|
+
arguments,
|
|
1200
|
+
function_body,
|
|
1201
|
+
called_on_null)
|
|
1202
|
+
end
|
|
1203
|
+
|
|
1204
|
+
def create_aggregate(aggregate_data, functions, types = nil)
|
|
1205
|
+
keyspace_name = aggregate_data['keyspace_name']
|
|
1206
|
+
aggregate_name = aggregate_data['aggregate_name']
|
|
1207
|
+
types ||= @schema.keyspace(keyspace_name).send(:raw_types)
|
|
1208
|
+
aggregate_type =
|
|
1209
|
+
@type_parser.parse(aggregate_data['return_type'], types).first
|
|
1210
|
+
argument_types = aggregate_data['argument_types'].map do |argument_type|
|
|
1211
|
+
@type_parser.parse(argument_type, types).first
|
|
1212
|
+
end.freeze
|
|
1213
|
+
state_type = @type_parser.parse(aggregate_data['state_type'], types).first
|
|
1214
|
+
initial_state = aggregate_data['initcond'] || 'null'
|
|
1215
|
+
|
|
1216
|
+
# The state-function takes arguments: first the stype, then the args of the
|
|
1217
|
+
# aggregate.
|
|
1218
|
+
state_function = functions.get(aggregate_data['state_func'],
|
|
1219
|
+
[state_type].concat(argument_types))
|
|
1220
|
+
|
|
1221
|
+
# The final-function takes an stype argument.
|
|
1222
|
+
final_function = functions.get(aggregate_data['final_func'],
|
|
1223
|
+
[state_type])
|
|
1224
|
+
|
|
1225
|
+
Aggregate.new(keyspace_name,
|
|
1226
|
+
aggregate_name,
|
|
1227
|
+
aggregate_type,
|
|
1228
|
+
argument_types,
|
|
1229
|
+
state_type,
|
|
1230
|
+
initial_state,
|
|
1231
|
+
state_function,
|
|
1232
|
+
final_function)
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1235
|
+
def create_types(rows_types, types)
|
|
1236
|
+
skipped_rows = ::Array.new
|
|
1237
|
+
|
|
1238
|
+
loop do
|
|
1239
|
+
rows_size = rows_types.size
|
|
1240
|
+
|
|
1241
|
+
until rows_types.empty?
|
|
1242
|
+
type_data = rows_types.shift
|
|
1243
|
+
type_name = type_data['type_name']
|
|
1244
|
+
type_keyspace = type_data['keyspace_name']
|
|
1245
|
+
type_fields = ::Array.new
|
|
1246
|
+
|
|
1247
|
+
begin
|
|
1248
|
+
field_names = type_data['field_names']
|
|
1249
|
+
field_types = type_data['field_types']
|
|
1250
|
+
field_names.each_with_index do |field_name, i|
|
|
1251
|
+
field_type = @type_parser.parse(field_types[i], types).first
|
|
1252
|
+
type_fields << [field_name, field_type]
|
|
1253
|
+
end
|
|
1254
|
+
|
|
1255
|
+
types[type_name] = Types.udt(type_keyspace, type_name, type_fields)
|
|
1256
|
+
rescue CQLTypeParser::IncompleteTypeError
|
|
1257
|
+
skipped_rows << type_data
|
|
1258
|
+
next
|
|
1259
|
+
end
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
break if skipped_rows.empty?
|
|
1263
|
+
|
|
1264
|
+
raise 'Unable to resolve circular references among UDTs when parsing' if rows_size == skipped_rows.size
|
|
1265
|
+
|
|
1266
|
+
rows_types, skipped_rows = skipped_rows, rows_types
|
|
1267
|
+
end
|
|
1268
|
+
end
|
|
1269
|
+
|
|
1270
|
+
def create_keyspace(keyspace_data, rows_tables, rows_columns, rows_types,
|
|
1271
|
+
rows_functions, rows_aggregates, rows_views, rows_indexes, rows_triggers)
|
|
1272
|
+
keyspace_name = keyspace_data['keyspace_name']
|
|
1273
|
+
replication = create_replication(keyspace_data)
|
|
1274
|
+
|
|
1275
|
+
types = ::Hash.new
|
|
1276
|
+
create_types(rows_types, types)
|
|
1277
|
+
|
|
1278
|
+
# Create a FunctionCollection for the functions and aggregates.
|
|
1279
|
+
functions = Cassandra::FunctionCollection.new
|
|
1280
|
+
rows_functions.each do |row|
|
|
1281
|
+
functions.add_or_update(create_function(row, types))
|
|
1282
|
+
end
|
|
1283
|
+
|
|
1284
|
+
aggregates = Cassandra::FunctionCollection.new
|
|
1285
|
+
rows_aggregates.each do |row|
|
|
1286
|
+
aggregates.add_or_update(create_aggregate(row, functions, types))
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1289
|
+
# lookup_columns is a hash of <table-name, rows_columns for that table>.
|
|
1290
|
+
# However, views are analogous to tables in this context, so we get
|
|
1291
|
+
# view columns organized by view-name also.
|
|
1292
|
+
|
|
1293
|
+
lookup_columns = map_rows_by(rows_columns, 'table_name')
|
|
1294
|
+
lookup_indexes = map_rows_by(rows_indexes, 'table_name')
|
|
1295
|
+
lookup_triggers = map_rows_by(rows_triggers, 'table_name')
|
|
1296
|
+
tables = rows_tables.each_with_object({}) do |row, h|
|
|
1297
|
+
table_name = row['table_name']
|
|
1298
|
+
h[table_name] = create_table(row, lookup_columns[table_name],
|
|
1299
|
+
lookup_indexes[table_name], lookup_triggers[table_name], types)
|
|
1300
|
+
end
|
|
1301
|
+
|
|
1302
|
+
views = rows_views.each_with_object({}) do |row, h|
|
|
1303
|
+
view_name = row['view_name']
|
|
1304
|
+
h[view_name] = create_materialized_view(row,
|
|
1305
|
+
lookup_columns[view_name],
|
|
1306
|
+
types)
|
|
1307
|
+
end
|
|
1308
|
+
|
|
1309
|
+
Keyspace.new(keyspace_name,
|
|
1310
|
+
keyspace_data['durable_writes'],
|
|
1311
|
+
replication,
|
|
1312
|
+
tables,
|
|
1313
|
+
types,
|
|
1314
|
+
functions,
|
|
1315
|
+
aggregates,
|
|
1316
|
+
views)
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
def create_replication(keyspace_data)
|
|
1320
|
+
options = keyspace_data['replication']
|
|
1321
|
+
klass = options.delete('class')
|
|
1322
|
+
klass.slice!(REPLICATION_PACKAGE_PREFIX)
|
|
1323
|
+
Keyspace::Replication.new(klass, options)
|
|
1324
|
+
end
|
|
1325
|
+
|
|
1326
|
+
def create_compaction_strategy(table_data)
|
|
1327
|
+
options = table_data['compaction'] || {}
|
|
1328
|
+
klass = options.delete('class') || ''
|
|
1329
|
+
klass.slice!('org.apache.cassandra.db.compaction.')
|
|
1330
|
+
ColumnContainer::Compaction.new(klass, options)
|
|
1331
|
+
end
|
|
1332
|
+
|
|
1333
|
+
def create_table_options(table_data, compaction_strategy, is_compact)
|
|
1334
|
+
compression = table_data['compression'] || {}
|
|
1335
|
+
compression['class'].slice!(COMPRESSION_PACKAGE_PREFIX) if compression['class']
|
|
1336
|
+
|
|
1337
|
+
Cassandra::ColumnContainer::Options.new(
|
|
1338
|
+
table_data['comment'],
|
|
1339
|
+
table_data['read_repair_chance'],
|
|
1340
|
+
table_data['dclocal_read_repair_chance'],
|
|
1341
|
+
table_data['gc_grace_seconds'],
|
|
1342
|
+
table_data['caching'],
|
|
1343
|
+
table_data['bloom_filter_fp_chance'],
|
|
1344
|
+
nil,
|
|
1345
|
+
table_data['memtable_flush_period_in_ms'],
|
|
1346
|
+
table_data['default_time_to_live'],
|
|
1347
|
+
table_data['speculative_retry'],
|
|
1348
|
+
nil,
|
|
1349
|
+
nil,
|
|
1350
|
+
table_data['min_index_interval'],
|
|
1351
|
+
table_data['max_index_interval'],
|
|
1352
|
+
compaction_strategy,
|
|
1353
|
+
compression,
|
|
1354
|
+
is_compact,
|
|
1355
|
+
table_data['crc_check_chance'],
|
|
1356
|
+
table_data['extensions'],
|
|
1357
|
+
table_data['cdc']
|
|
1358
|
+
)
|
|
1359
|
+
end
|
|
1360
|
+
|
|
1361
|
+
def create_column(column_data, types)
|
|
1362
|
+
name = column_data['column_name']
|
|
1363
|
+
is_static = column_data['kind'].to_s.casecmp('STATIC').zero?
|
|
1364
|
+
order = column_data['clustering_order'] == 'desc' ? :desc : :asc
|
|
1365
|
+
if column_data['type'][0] == "'"
|
|
1366
|
+
# This is a custom column type.
|
|
1367
|
+
type = Types.custom(column_data['type'].slice(1, column_data['type'].length - 2))
|
|
1368
|
+
is_frozen = false
|
|
1369
|
+
else
|
|
1370
|
+
type, is_frozen = @type_parser.parse(column_data['type'], types)
|
|
1371
|
+
end
|
|
1372
|
+
|
|
1373
|
+
Column.new(name, type, order, is_static, is_frozen)
|
|
1374
|
+
end
|
|
1375
|
+
|
|
1376
|
+
def create_table(table_data, rows_columns, rows_indexes, rows_triggers, types = nil)
|
|
1377
|
+
keyspace_name = table_data['keyspace_name']
|
|
1378
|
+
table_name = table_data['table_name']
|
|
1379
|
+
table_flags = table_data['flags']
|
|
1380
|
+
|
|
1381
|
+
is_dense = table_flags.include?('dense')
|
|
1382
|
+
is_super = table_flags.include?('super')
|
|
1383
|
+
is_compound = table_flags.include?('compound')
|
|
1384
|
+
is_compact = is_super || is_dense || !is_compound
|
|
1385
|
+
is_static_compact = !is_super && !is_dense && !is_compound
|
|
1386
|
+
|
|
1387
|
+
# Separate out partition-key, clustering columns, other columns.
|
|
1388
|
+
partition_key = []
|
|
1389
|
+
clustering_columns = []
|
|
1390
|
+
clustering_order = []
|
|
1391
|
+
other_columns = []
|
|
1392
|
+
types ||= @schema.keyspace(keyspace_name).send(:raw_types)
|
|
1393
|
+
|
|
1394
|
+
rows_columns.each do |row|
|
|
1395
|
+
next if row['column_name'].empty?
|
|
1396
|
+
|
|
1397
|
+
kind = row['kind'].to_s
|
|
1398
|
+
index = row['position'] || 0
|
|
1399
|
+
|
|
1400
|
+
if is_static_compact
|
|
1401
|
+
if kind.casecmp('CLUSTERING').zero? || kind.casecmp('REGULAR').zero?
|
|
1402
|
+
# Skip clustering columns in static-compact tables; they are internal to C*.
|
|
1403
|
+
# Oddly so are regular columns.
|
|
1404
|
+
next
|
|
1405
|
+
end
|
|
1406
|
+
if kind.casecmp('STATIC').zero?
|
|
1407
|
+
# Coerce static type to regular.
|
|
1408
|
+
kind = 'REGULAR'
|
|
1409
|
+
row['kind'] = 'regular'
|
|
1410
|
+
end
|
|
1411
|
+
end
|
|
1412
|
+
|
|
1413
|
+
column = create_column(row, types)
|
|
1414
|
+
case kind.upcase
|
|
1415
|
+
when 'PARTITION_KEY'
|
|
1416
|
+
partition_key[index] = column
|
|
1417
|
+
when 'CLUSTERING'
|
|
1418
|
+
clustering_columns[index] = column
|
|
1419
|
+
clustering_order[index] = column.order
|
|
1420
|
+
else
|
|
1421
|
+
other_columns << column
|
|
1422
|
+
end
|
|
1423
|
+
end
|
|
1424
|
+
|
|
1425
|
+
# Default the crc_check_chance to 1.0 (Java driver does this, so we
|
|
1426
|
+
# should, too).
|
|
1427
|
+
table_data['crc_check_chance'] ||= 1.0
|
|
1428
|
+
compaction_strategy = create_compaction_strategy(table_data)
|
|
1429
|
+
table_options =
|
|
1430
|
+
create_table_options(table_data, compaction_strategy, is_compact)
|
|
1431
|
+
|
|
1432
|
+
table = Cassandra::Table.new(@schema.keyspace(keyspace_name),
|
|
1433
|
+
table_name,
|
|
1434
|
+
partition_key,
|
|
1435
|
+
clustering_columns,
|
|
1436
|
+
other_columns,
|
|
1437
|
+
table_options,
|
|
1438
|
+
clustering_order,
|
|
1439
|
+
table_data['id'])
|
|
1440
|
+
rows_indexes.each do |row|
|
|
1441
|
+
create_index(table, row)
|
|
1442
|
+
end
|
|
1443
|
+
|
|
1444
|
+
# Create Trigger objects and add them to the table.
|
|
1445
|
+
rows_triggers.each do |row_trigger|
|
|
1446
|
+
table.add_trigger(Cassandra::Trigger.new(table,
|
|
1447
|
+
row_trigger['trigger_name'],
|
|
1448
|
+
row_trigger['options']))
|
|
1449
|
+
end
|
|
1450
|
+
|
|
1451
|
+
table
|
|
1452
|
+
end
|
|
1453
|
+
|
|
1454
|
+
def create_index(table, row_index)
|
|
1455
|
+
options = row_index['options']
|
|
1456
|
+
table.add_index(Cassandra::Index.new(table, row_index['index_name'],
|
|
1457
|
+
row_index['kind'].downcase.to_sym,
|
|
1458
|
+
options['target'], options))
|
|
1459
|
+
end
|
|
1460
|
+
|
|
1461
|
+
def create_materialized_view(view_data, rows_columns, types = nil)
|
|
1462
|
+
keyspace_name = view_data['keyspace_name']
|
|
1463
|
+
view_name = view_data['view_name']
|
|
1464
|
+
base_table_name = view_data['base_table_name']
|
|
1465
|
+
include_all_columns = view_data['include_all_columns']
|
|
1466
|
+
where_clause = view_data['where_clause']
|
|
1467
|
+
|
|
1468
|
+
# Separate out partition key, clustering columns, other columns
|
|
1469
|
+
partition_key = []
|
|
1470
|
+
clustering_columns = []
|
|
1471
|
+
other_columns = []
|
|
1472
|
+
types ||= @schema.keyspace(keyspace_name).send(:raw_types)
|
|
1473
|
+
|
|
1474
|
+
rows_columns.each do |row|
|
|
1475
|
+
next if row['column_name'].empty?
|
|
1476
|
+
|
|
1477
|
+
column = create_column(row, types)
|
|
1478
|
+
kind = row['kind'].to_s
|
|
1479
|
+
index = row['position'] || 0
|
|
1480
|
+
|
|
1481
|
+
case kind.upcase
|
|
1482
|
+
when 'PARTITION_KEY'
|
|
1483
|
+
partition_key[index] = column
|
|
1484
|
+
when 'CLUSTERING'
|
|
1485
|
+
clustering_columns[index] = column
|
|
1486
|
+
else
|
|
1487
|
+
other_columns << column
|
|
1488
|
+
end
|
|
1489
|
+
end
|
|
1490
|
+
|
|
1491
|
+
compaction_strategy = create_compaction_strategy(view_data)
|
|
1492
|
+
view_options = create_table_options(view_data, compaction_strategy, false)
|
|
1493
|
+
|
|
1494
|
+
MaterializedView.new(@schema.keyspace(keyspace_name),
|
|
1495
|
+
view_name,
|
|
1496
|
+
partition_key,
|
|
1497
|
+
clustering_columns,
|
|
1498
|
+
other_columns,
|
|
1499
|
+
view_options,
|
|
1500
|
+
include_all_columns,
|
|
1501
|
+
where_clause,
|
|
1502
|
+
base_table_name,
|
|
1503
|
+
view_data['id'])
|
|
1504
|
+
end
|
|
1505
|
+
end
|
|
1506
|
+
|
|
1507
|
+
class MultiVersion
|
|
1508
|
+
class Version
|
|
1509
|
+
def initialize(version, constructor)
|
|
1510
|
+
@version = version
|
|
1511
|
+
@constructor = constructor
|
|
1512
|
+
@fetcher = nil
|
|
1513
|
+
end
|
|
1514
|
+
|
|
1515
|
+
def matches?(version)
|
|
1516
|
+
version.start_with?(@version)
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1519
|
+
def fetcher
|
|
1520
|
+
@fetcher ||= @constructor.call
|
|
1521
|
+
end
|
|
1522
|
+
end
|
|
1523
|
+
|
|
1524
|
+
def initialize(registry)
|
|
1525
|
+
@registry = registry
|
|
1526
|
+
@versions = []
|
|
1527
|
+
@fetchers = {}
|
|
1528
|
+
end
|
|
1529
|
+
|
|
1530
|
+
def when(version, &block)
|
|
1531
|
+
@versions << Version.new(version, block)
|
|
1532
|
+
end
|
|
1533
|
+
|
|
1534
|
+
def fetch(connection)
|
|
1535
|
+
find_fetcher(connection)
|
|
1536
|
+
.fetch(connection)
|
|
1537
|
+
rescue => e
|
|
1538
|
+
return Ione::Future.failed(e)
|
|
1539
|
+
end
|
|
1540
|
+
|
|
1541
|
+
def fetch_keyspace(connection, keyspace_name)
|
|
1542
|
+
find_fetcher(connection)
|
|
1543
|
+
.fetch_keyspace(connection, keyspace_name)
|
|
1544
|
+
rescue => e
|
|
1545
|
+
return Ione::Future.failed(e)
|
|
1546
|
+
end
|
|
1547
|
+
|
|
1548
|
+
def fetch_table(connection, keyspace_name, table_name)
|
|
1549
|
+
find_fetcher(connection)
|
|
1550
|
+
.fetch_table(connection, keyspace_name, table_name)
|
|
1551
|
+
rescue => e
|
|
1552
|
+
return Ione::Future.failed(e)
|
|
1553
|
+
end
|
|
1554
|
+
|
|
1555
|
+
def fetch_materialized_view(connection, keyspace_name, view_name)
|
|
1556
|
+
find_fetcher(connection)
|
|
1557
|
+
.fetch_materialized_view(connection, keyspace_name, view_name)
|
|
1558
|
+
rescue => e
|
|
1559
|
+
return Ione::Future.failed(e)
|
|
1560
|
+
end
|
|
1561
|
+
|
|
1562
|
+
def fetch_type(connection, keyspace_name, type_name)
|
|
1563
|
+
find_fetcher(connection)
|
|
1564
|
+
.fetch_type(connection, keyspace_name, type_name)
|
|
1565
|
+
rescue => e
|
|
1566
|
+
return Ione::Future.failed(e)
|
|
1567
|
+
end
|
|
1568
|
+
|
|
1569
|
+
def fetch_function(connection, keyspace_name, function_name, function_args)
|
|
1570
|
+
find_fetcher(connection)
|
|
1571
|
+
.fetch_function(connection, keyspace_name, function_name, function_args)
|
|
1572
|
+
rescue => e
|
|
1573
|
+
return Ione::Future.failed(e)
|
|
1574
|
+
end
|
|
1575
|
+
|
|
1576
|
+
def fetch_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
|
|
1577
|
+
find_fetcher(connection)
|
|
1578
|
+
.fetch_aggregate(connection, keyspace_name, aggregate_name, aggregate_args)
|
|
1579
|
+
rescue => e
|
|
1580
|
+
return Ione::Future.failed(e)
|
|
1581
|
+
end
|
|
1582
|
+
|
|
1583
|
+
# parse an array of string argument types and return an array of
|
|
1584
|
+
# [Cassandra::Type]s.
|
|
1585
|
+
# @param connection a connection to a Cassandra node.
|
|
1586
|
+
# @param keyspace_name [String] name of the keyspace.
|
|
1587
|
+
# @param argument_types [Array<String>] array of argument types.
|
|
1588
|
+
# @return [Array<Cassandra::Type>] array of parsed types.
|
|
1589
|
+
def parse_argument_types(connection, keyspace_name, argument_types)
|
|
1590
|
+
find_fetcher(connection).parse_argument_types(connection,
|
|
1591
|
+
keyspace_name,
|
|
1592
|
+
argument_types)
|
|
1593
|
+
end
|
|
1594
|
+
|
|
1595
|
+
private
|
|
1596
|
+
|
|
1597
|
+
def find_fetcher(connection)
|
|
1598
|
+
host = @registry.host(connection.host)
|
|
1599
|
+
|
|
1600
|
+
unless host
|
|
1601
|
+
ips = @registry.hosts.map(&:ip)
|
|
1602
|
+
raise Errors::ClientError,
|
|
1603
|
+
'unable to find release version for current host, ' \
|
|
1604
|
+
"connected to #{connection.host}, but cluster contains " \
|
|
1605
|
+
"#{ips}."
|
|
1606
|
+
end
|
|
1607
|
+
|
|
1608
|
+
version = host.release_version
|
|
1609
|
+
unless version
|
|
1610
|
+
raise Errors::ClientError, 'unable to determine release ' \
|
|
1611
|
+
"version for host: #{host.inspect}"
|
|
1612
|
+
end
|
|
1613
|
+
|
|
1614
|
+
@fetchers[version] ||= begin
|
|
1615
|
+
current = @versions.find {|v| v.matches?(version)}
|
|
1616
|
+
unless current
|
|
1617
|
+
raise Errors::ClientError, 'unsupported release version ' \
|
|
1618
|
+
"#{version.inspect}."
|
|
1619
|
+
end
|
|
1620
|
+
current.fetcher
|
|
1621
|
+
end
|
|
1622
|
+
end
|
|
1623
|
+
end
|
|
1624
|
+
end
|
|
1625
|
+
end
|
|
1626
|
+
end
|
|
1627
|
+
end
|