cequel 2.1.0 → 3.0.0
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 +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +2 -2
- data/README.md +8 -0
- data/lib/cequel/errors.rb +2 -0
- data/lib/cequel/metal/new_relic_instrumentation.rb +2 -1
- data/lib/cequel/record.rb +8 -0
- data/lib/cequel/record/schema.rb +30 -23
- data/lib/cequel/schema.rb +3 -2
- data/lib/cequel/schema/column.rb +11 -1
- data/lib/cequel/schema/keyspace.rb +18 -7
- data/lib/cequel/schema/patch.rb +152 -0
- data/lib/cequel/schema/table.rb +55 -137
- data/lib/cequel/schema/table_desc_dsl.rb +196 -0
- data/lib/cequel/schema/table_differ.rb +112 -0
- data/lib/cequel/schema/table_property.rb +14 -0
- data/lib/cequel/schema/table_reader.rb +81 -85
- data/lib/cequel/schema/table_updater.rb +0 -17
- data/lib/cequel/schema/table_writer.rb +10 -9
- data/lib/cequel/version.rb +1 -1
- data/spec/examples/metal/data_set_spec.rb +156 -153
- data/spec/examples/metal/keyspace_spec.rb +4 -4
- data/spec/examples/record/associations_spec.rb +6 -0
- data/spec/examples/record/mass_assignment_spec.rb +2 -2
- data/spec/examples/record/properties_spec.rb +1 -0
- data/spec/examples/record/record_set_spec.rb +1 -1
- data/spec/examples/schema/patch_spec.rb +190 -0
- data/spec/examples/schema/table_differ_spec.rb +280 -0
- data/spec/examples/schema/table_reader_spec.rb +379 -354
- data/spec/examples/schema/table_updater_spec.rb +0 -12
- data/spec/examples/spec_helper.rb +5 -5
- data/spec/examples/spec_support/preparation_spec.rb +4 -0
- data/spec/support/helpers.rb +23 -0
- metadata +9 -6
- data/lib/cequel/schema/create_table_dsl.rb +0 -88
- data/lib/cequel/schema/table_synchronizer.rb +0 -180
- data/spec/examples/schema/table_synchronizer_spec.rb +0 -200
@@ -8,63 +8,77 @@ describe Cequel::Schema::TableReader do
|
|
8
8
|
cequel.schema.drop_table(table_name)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
describe 'reading simple key' do
|
11
|
+
describe ".read(keyspace, table_name)" do
|
14
12
|
before do
|
15
13
|
cequel.execute("CREATE TABLE #{table_name} (permalink text PRIMARY KEY)")
|
16
14
|
cequel.send(:cluster).refresh_schema
|
17
15
|
end
|
18
16
|
|
19
|
-
it
|
20
|
-
expect(
|
17
|
+
it "returns a table" do
|
18
|
+
expect(
|
19
|
+
described_class.read(cequel, table_name)
|
20
|
+
).to be_kind_of Cequel::Schema::Table
|
21
21
|
end
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
24
|
+
describe "#call" do
|
25
|
+
let(:table) { described_class.new(fetch_table_data).call }
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
context 'simple key' do
|
28
|
+
before do
|
29
|
+
cequel.execute("CREATE TABLE #{table_name} (permalink text PRIMARY KEY)")
|
30
|
+
cequel.send(:cluster).refresh_schema
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
it 'should read name correctly' do
|
34
|
+
expect(table.partition_key_columns.first.name).to eq(:permalink)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should read type correctly' do
|
38
|
+
expect(table.partition_key_columns.first.type).to be_a(Cequel::Type::Text)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should have no nonpartition keys' do
|
42
|
+
expect(table.clustering_columns).to be_empty
|
43
|
+
end
|
44
|
+
end # context 'simple key'
|
45
|
+
|
46
|
+
context 'single cluster key' do
|
47
|
+
before do
|
48
|
+
cequel.execute <<-CQL
|
35
49
|
CREATE TABLE #{table_name} (
|
36
50
|
blog_subdomain text,
|
37
51
|
permalink ascii,
|
38
52
|
PRIMARY KEY (blog_subdomain, permalink)
|
39
53
|
)
|
40
54
|
CQL
|
41
|
-
|
55
|
+
end
|
42
56
|
|
43
|
-
|
44
|
-
|
45
|
-
|
57
|
+
it 'should read partition key name' do
|
58
|
+
expect(table.partition_key_columns.map(&:name)).to eq([:blog_subdomain])
|
59
|
+
end
|
46
60
|
|
47
|
-
|
48
|
-
|
49
|
-
|
61
|
+
it 'should read partition key type' do
|
62
|
+
expect(table.partition_key_columns.map(&:type)).to eq([Cequel::Type::Text.instance])
|
63
|
+
end
|
50
64
|
|
51
|
-
|
52
|
-
|
53
|
-
|
65
|
+
it 'should read non-partition key name' do
|
66
|
+
expect(table.clustering_columns.map(&:name)).to eq([:permalink])
|
67
|
+
end
|
54
68
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
69
|
+
it 'should read non-partition key type' do
|
70
|
+
expect(table.clustering_columns.map(&:type)).
|
71
|
+
to eq([Cequel::Type::Ascii.instance])
|
72
|
+
end
|
59
73
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
74
|
+
it 'should default clustering order to asc' do
|
75
|
+
expect(table.clustering_columns.map(&:clustering_order)).to eq([:asc])
|
76
|
+
end
|
77
|
+
end # context 'single cluster key'
|
64
78
|
|
65
|
-
|
66
|
-
|
67
|
-
|
79
|
+
context 'reverse-ordered cluster key' do
|
80
|
+
before do
|
81
|
+
cequel.execute <<-CQL
|
68
82
|
CREATE TABLE #{table_name} (
|
69
83
|
blog_subdomain text,
|
70
84
|
permalink ascii,
|
@@ -72,25 +86,25 @@ describe Cequel::Schema::TableReader do
|
|
72
86
|
)
|
73
87
|
WITH CLUSTERING ORDER BY (permalink DESC)
|
74
88
|
CQL
|
75
|
-
|
89
|
+
end
|
76
90
|
|
77
|
-
|
78
|
-
|
79
|
-
|
91
|
+
it 'should read non-partition key name' do
|
92
|
+
expect(table.clustering_columns.map(&:name)).to eq([:permalink])
|
93
|
+
end
|
80
94
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
95
|
+
it 'should read non-partition key type' do
|
96
|
+
expect(table.clustering_columns.map(&:type)).
|
97
|
+
to eq([Cequel::Type::Ascii.instance])
|
98
|
+
end
|
85
99
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
100
|
+
it 'should recognize reversed clustering order' do
|
101
|
+
expect(table.clustering_columns.map(&:clustering_order)).to eq([:desc])
|
102
|
+
end
|
103
|
+
end # context 'reverse-ordered cluster key'
|
90
104
|
|
91
|
-
|
92
|
-
|
93
|
-
|
105
|
+
context 'compound cluster key' do
|
106
|
+
before do
|
107
|
+
cequel.execute <<-CQL
|
94
108
|
CREATE TABLE #{table_name} (
|
95
109
|
blog_subdomain text,
|
96
110
|
permalink ascii,
|
@@ -99,51 +113,51 @@ describe Cequel::Schema::TableReader do
|
|
99
113
|
)
|
100
114
|
WITH CLUSTERING ORDER BY (permalink DESC, author_id ASC)
|
101
115
|
CQL
|
102
|
-
|
116
|
+
end
|
103
117
|
|
104
|
-
|
105
|
-
|
106
|
-
|
118
|
+
it 'should read non-partition key names' do
|
119
|
+
expect(table.clustering_columns.map(&:name)).to eq([:permalink, :author_id])
|
120
|
+
end
|
107
121
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
122
|
+
it 'should read non-partition key types' do
|
123
|
+
expect(table.clustering_columns.map(&:type)).
|
124
|
+
to eq([Cequel::Type::Ascii.instance, Cequel::Type::Uuid.instance])
|
125
|
+
end
|
112
126
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
127
|
+
it 'should read heterogeneous clustering orders' do
|
128
|
+
expect(table.clustering_columns.map(&:clustering_order)).to eq([:desc, :asc])
|
129
|
+
end
|
130
|
+
end # context 'compound context key'
|
117
131
|
|
118
|
-
|
119
|
-
|
120
|
-
|
132
|
+
context 'compound partition key' do
|
133
|
+
before do
|
134
|
+
cequel.execute <<-CQL
|
121
135
|
CREATE TABLE #{table_name} (
|
122
136
|
blog_subdomain text,
|
123
137
|
permalink ascii,
|
124
138
|
PRIMARY KEY ((blog_subdomain, permalink))
|
125
139
|
)
|
126
140
|
CQL
|
127
|
-
|
141
|
+
end
|
128
142
|
|
129
|
-
|
130
|
-
|
131
|
-
|
143
|
+
it 'should read partition key names' do
|
144
|
+
expect(table.partition_key_columns.map(&:name)).to eq([:blog_subdomain, :permalink])
|
145
|
+
end
|
132
146
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
147
|
+
it 'should read partition key types' do
|
148
|
+
expect(table.partition_key_columns.map(&:type)).
|
149
|
+
to eq([Cequel::Type::Text.instance, Cequel::Type::Ascii.instance])
|
150
|
+
end
|
137
151
|
|
138
|
-
|
139
|
-
|
140
|
-
|
152
|
+
it 'should have empty nonpartition keys' do
|
153
|
+
expect(table.clustering_columns).to be_empty
|
154
|
+
end
|
141
155
|
|
142
|
-
|
156
|
+
end # context 'compound partition key'
|
143
157
|
|
144
|
-
|
145
|
-
|
146
|
-
|
158
|
+
context 'compound partition and cluster keys' do
|
159
|
+
before do
|
160
|
+
cequel.execute <<-CQL
|
147
161
|
CREATE TABLE #{table_name} (
|
148
162
|
blog_subdomain text,
|
149
163
|
permalink ascii,
|
@@ -153,283 +167,294 @@ describe Cequel::Schema::TableReader do
|
|
153
167
|
)
|
154
168
|
WITH CLUSTERING ORDER BY (author_id ASC, published_at DESC)
|
155
169
|
CQL
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should read partition key names' do
|
173
|
+
expect(table.partition_key_columns.map(&:name)).to eq([:blog_subdomain, :permalink])
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should read partition key types' do
|
177
|
+
expect(table.partition_key_columns.map(&:type)).
|
178
|
+
to eq([Cequel::Type::Text.instance, Cequel::Type::Ascii.instance])
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should read non-partition key names' do
|
182
|
+
expect(table.clustering_columns.map(&:name)).
|
183
|
+
to eq([:author_id, :published_at])
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should read non-partition key types' do
|
187
|
+
expect(table.clustering_columns.map(&:type)).to eq(
|
188
|
+
[Cequel::Type::Uuid.instance, Cequel::Type::Timestamp.instance]
|
189
|
+
)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should read clustering order' do
|
193
|
+
expect(table.clustering_columns.map(&:clustering_order)).to eq([:asc, :desc])
|
194
|
+
end
|
195
|
+
|
196
|
+
end # context 'compound partition and context keys'
|
197
|
+
|
198
|
+
context 'data columns' do
|
199
|
+
|
200
|
+
before do
|
201
|
+
cequel.execute <<-CQL
|
202
|
+
CREATE TABLE #{table_name} (
|
203
|
+
blog_subdomain text,
|
204
|
+
permalink ascii,
|
205
|
+
title text,
|
206
|
+
author_id uuid,
|
207
|
+
categories LIST <text>,
|
208
|
+
tags SET <text>,
|
209
|
+
trackbacks MAP <timestamp,ascii>,
|
210
|
+
PRIMARY KEY (blog_subdomain, permalink)
|
211
|
+
)
|
212
|
+
CQL
|
213
|
+
cequel.execute("CREATE INDEX posts_author_id_idx ON #{table_name} (author_id)")
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should read types of scalar data columns' do
|
217
|
+
expect(table.data_columns.find { |column| column.name == :title }.type).
|
218
|
+
to eq(Cequel::Type[:text])
|
219
|
+
expect(table.data_columns.find { |column| column.name == :author_id }.type).
|
220
|
+
to eq(Cequel::Type[:uuid])
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should read index attributes' do
|
224
|
+
expect(table.data_columns.find { |column| column.name == :author_id }.index_name).
|
225
|
+
to eq(:posts_author_id_idx)
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'should leave nil index for non-indexed columns' do
|
229
|
+
expect(table.data_columns.find { |column| column.name == :title }.index_name).
|
230
|
+
to be_nil
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should read list columns' do
|
234
|
+
expect(table.data_columns.find { |column| column.name == :categories }).
|
235
|
+
to be_a(Cequel::Schema::List)
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should read list column type' do
|
239
|
+
expect(table.data_columns.find { |column| column.name == :categories }.type).
|
240
|
+
to eq(Cequel::Type[:text])
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should read set columns' do
|
244
|
+
expect(table.data_columns.find { |column| column.name == :tags }).
|
245
|
+
to be_a(Cequel::Schema::Set)
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'should read set column type' do
|
249
|
+
expect(table.data_columns.find { |column| column.name == :tags }.type).
|
250
|
+
to eq(Cequel::Type[:text])
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should read map columns' do
|
254
|
+
expect(table.data_columns.find { |column| column.name == :trackbacks }).
|
255
|
+
to be_a(Cequel::Schema::Map)
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'should read map column key type' do
|
259
|
+
expect(table.data_columns.find { |column| column.name == :trackbacks }.key_type).
|
260
|
+
to eq(Cequel::Type[:timestamp])
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'should read map column value type' do
|
264
|
+
expect(table.data_columns.find { |column| column.name == :trackbacks }.
|
265
|
+
value_type).to eq(Cequel::Type[:ascii])
|
266
|
+
end
|
267
|
+
|
268
|
+
end # context 'data columns'
|
269
|
+
|
270
|
+
context 'storage properties' do
|
271
|
+
|
272
|
+
before do
|
273
|
+
cequel.execute <<-CQL
|
274
|
+
CREATE TABLE #{table_name} (permalink text PRIMARY KEY)
|
275
|
+
WITH bloom_filter_fp_chance = 0.02
|
276
|
+
AND comment = 'Posts table'
|
277
|
+
AND compaction = {
|
278
|
+
'class' : 'SizeTieredCompactionStrategy',
|
279
|
+
'bucket_high' : 1.8,
|
280
|
+
'max_threshold' : 64,
|
281
|
+
'min_sstable_size' : 50,
|
282
|
+
'tombstone_compaction_interval' : 2
|
283
|
+
} AND compression = {
|
284
|
+
'sstable_compression' : 'DeflateCompressor',
|
285
|
+
'chunk_length_kb' : 128,
|
286
|
+
'crc_check_chance' : 0.5
|
287
|
+
}
|
288
|
+
CQL
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'should read float properties' do
|
292
|
+
expect(table.property(:bloom_filter_fp_chance)).to eq(0.02)
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'should read string properties' do
|
296
|
+
expect(table.property(:comment)).to eq('Posts table')
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'should read and simplify compaction class' do
|
300
|
+
expect(table.property(:compaction)[:class]).
|
301
|
+
to eq('SizeTieredCompactionStrategy')
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'should read float properties from compaction hash' do
|
305
|
+
expect(table.property(:compaction)[:bucket_high]).to eq(1.8)
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'should read integer properties from compaction hash' do
|
309
|
+
expect(table.property(:compaction)[:max_threshold]).to eq(64)
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'should read and simplify compression class' do
|
313
|
+
expect(table.property(:compression)[:sstable_compression] ||
|
314
|
+
table.property(:compression)[:class]).
|
315
|
+
to eq('DeflateCompressor')
|
316
|
+
end
|
317
|
+
|
318
|
+
it 'should read integer properties from compression class' do
|
319
|
+
expect(table.property(:compression)[:chunk_length_kb]).to eq(128)
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'should read float properties from compression class' do
|
323
|
+
expect(table.property(:compression)[:crc_check_chance]).to eq(0.5)
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'should recognize no compact storage' do
|
327
|
+
expect(table).not_to be_compact_storage
|
328
|
+
end
|
329
|
+
end # context 'storage properties'
|
330
|
+
|
331
|
+
context 'skinny-row compact storage' do
|
332
|
+
before do
|
333
|
+
cequel.execute <<-CQL
|
334
|
+
CREATE TABLE #{table_name} (permalink text PRIMARY KEY, title text, body text)
|
335
|
+
WITH COMPACT STORAGE
|
336
|
+
CQL
|
337
|
+
end
|
338
|
+
subject { table }
|
339
|
+
|
340
|
+
it { is_expected.to be_compact_storage }
|
341
|
+
its(:partition_key_columns) { should ==
|
342
|
+
[Cequel::Schema::PartitionKey.new(:permalink, :text)] }
|
343
|
+
its(:clustering_columns) { should be_empty }
|
344
|
+
specify { expect(table.data_columns).to contain_exactly(
|
345
|
+
Cequel::Schema::DataColumn.new(:title, :text),
|
346
|
+
Cequel::Schema::DataColumn.new(:body, :text)) }
|
347
|
+
end
|
348
|
+
|
349
|
+
context 'wide-row compact storage' do
|
350
|
+
before do
|
351
|
+
cequel.execute <<-CQL
|
352
|
+
CREATE TABLE #{table_name} (
|
353
|
+
blog_subdomain text,
|
354
|
+
id uuid,
|
355
|
+
data text,
|
356
|
+
PRIMARY KEY (blog_subdomain, id)
|
357
|
+
)
|
358
|
+
WITH COMPACT STORAGE
|
359
|
+
CQL
|
360
|
+
end
|
361
|
+
subject { table }
|
362
|
+
|
363
|
+
it { is_expected.to be_compact_storage }
|
364
|
+
its(:partition_key_columns) { should ==
|
365
|
+
[Cequel::Schema::PartitionKey.new(:blog_subdomain, :text)] }
|
366
|
+
its(:clustering_columns) { should ==
|
367
|
+
[Cequel::Schema::ClusteringColumn.new(:id, :uuid)] }
|
368
|
+
its(:data_columns) { should ==
|
369
|
+
[Cequel::Schema::DataColumn.new(:data, :text)] }
|
370
|
+
end
|
371
|
+
|
372
|
+
context 'materialized view exists', cql: '~> 3.4' do
|
373
|
+
let!(:name) { table_name }
|
374
|
+
let(:view_name) { "#{name}_view" }
|
375
|
+
before do
|
376
|
+
cequel.execute <<-CQL
|
377
|
+
CREATE TABLE #{table_name} (
|
378
|
+
blog_subdomain text,
|
379
|
+
permalink ascii,
|
380
|
+
PRIMARY KEY (blog_subdomain, permalink)
|
381
|
+
)
|
382
|
+
CQL
|
383
|
+
cequel.execute <<-CQL
|
384
|
+
CREATE MATERIALIZED VIEW #{view_name} AS
|
385
|
+
SELECT blog_subdomain, permalink
|
386
|
+
FROM #{name}
|
387
|
+
WHERE blog_subdomain IS NOT NULL AND permalink IS NOT NULL
|
388
|
+
PRIMARY KEY ( blog_subdomain, permalink )
|
389
|
+
CQL
|
390
|
+
end
|
391
|
+
after do
|
392
|
+
cequel.schema.drop_materialized_view(view_name)
|
393
|
+
end
|
394
|
+
|
395
|
+
let(:view) { described_class.new(fetch_view_data).call }
|
396
|
+
|
397
|
+
it "recognizes that regular tables are not views" do
|
398
|
+
expect( table.materialized_view? ).to be false
|
399
|
+
end
|
400
|
+
|
401
|
+
it "recognizes thats view tables are views" do
|
402
|
+
expect( view.materialized_view? ).to be true
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
context 'skinny-row legacy table', thrift: true do
|
407
|
+
before do
|
408
|
+
legacy_connection.execute <<-CQL
|
409
|
+
CREATE TABLE #{table_name} (permalink text PRIMARY KEY, title text, body text)
|
410
|
+
CQL
|
411
|
+
end
|
412
|
+
subject { table }
|
413
|
+
|
414
|
+
it { is_expected.to be_compact_storage }
|
415
|
+
its(:partition_key_columns) { is_expected.to eq(
|
416
|
+
[Cequel::Schema::PartitionKey.new(:permalink, :text)]
|
417
|
+
) }
|
418
|
+
its(:clustering_columns) { is_expected.to be_empty }
|
419
|
+
its(:data_columns) { is_expected.to match_array(
|
420
|
+
[Cequel::Schema::DataColumn.new(:title, :text),
|
421
|
+
Cequel::Schema::DataColumn.new(:body, :text)]
|
422
|
+
) }
|
423
|
+
end
|
424
|
+
|
425
|
+
context 'wide-row legacy table', thrift: true do
|
426
|
+
before do
|
427
|
+
legacy_connection.execute(<<-CQL2)
|
428
|
+
CREATE COLUMNFAMILY #{table_name} (blog_subdomain text PRIMARY KEY)
|
429
|
+
WITH comparator=uuid AND default_validation=text
|
430
|
+
CQL2
|
431
|
+
end
|
432
|
+
subject { table }
|
433
|
+
|
434
|
+
it { is_expected.to be_compact_storage }
|
435
|
+
its(:partition_key_columns) { is_expected.to eq(
|
436
|
+
[Cequel::Schema::PartitionKey.new(:blog_subdomain, :text)]
|
437
|
+
) }
|
438
|
+
its(:clustering_columns) { is_expected.to eq(
|
439
|
+
[Cequel::Schema::ClusteringColumn.new(:column1, :uuid)]
|
440
|
+
) }
|
441
|
+
its(:data_columns) { is_expected.to eq(
|
442
|
+
[Cequel::Schema::DataColumn.new(:value, :text)]
|
443
|
+
) }
|
156
444
|
end
|
157
|
-
|
158
|
-
it 'should read partition key names' do
|
159
|
-
expect(table.partition_key_columns.map(&:name)).to eq([:blog_subdomain, :permalink])
|
160
|
-
end
|
161
|
-
|
162
|
-
it 'should read partition key types' do
|
163
|
-
expect(table.partition_key_columns.map(&:type)).
|
164
|
-
to eq([Cequel::Type::Text.instance, Cequel::Type::Ascii.instance])
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'should read non-partition key names' do
|
168
|
-
expect(table.clustering_columns.map(&:name)).
|
169
|
-
to eq([:author_id, :published_at])
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'should read non-partition key types' do
|
173
|
-
expect(table.clustering_columns.map(&:type)).to eq(
|
174
|
-
[Cequel::Type::Uuid.instance, Cequel::Type::Timestamp.instance]
|
175
|
-
)
|
176
|
-
end
|
177
|
-
|
178
|
-
it 'should read clustering order' do
|
179
|
-
expect(table.clustering_columns.map(&:clustering_order)).to eq([:asc, :desc])
|
180
|
-
end
|
181
|
-
|
182
|
-
end # describe 'reading compound partition and non-partition keys'
|
183
|
-
|
184
|
-
describe 'reading data columns' do
|
185
|
-
|
186
|
-
before do
|
187
|
-
cequel.execute <<-CQL
|
188
|
-
CREATE TABLE #{table_name} (
|
189
|
-
blog_subdomain text,
|
190
|
-
permalink ascii,
|
191
|
-
title text,
|
192
|
-
author_id uuid,
|
193
|
-
categories LIST <text>,
|
194
|
-
tags SET <text>,
|
195
|
-
trackbacks MAP <timestamp,ascii>,
|
196
|
-
PRIMARY KEY (blog_subdomain, permalink)
|
197
|
-
)
|
198
|
-
CQL
|
199
|
-
cequel.execute("CREATE INDEX posts_author_id_idx ON #{table_name} (author_id)")
|
200
|
-
end
|
201
|
-
|
202
|
-
it 'should read types of scalar data columns' do
|
203
|
-
expect(table.data_columns.find { |column| column.name == :title }.type).
|
204
|
-
to eq(Cequel::Type[:text])
|
205
|
-
expect(table.data_columns.find { |column| column.name == :author_id }.type).
|
206
|
-
to eq(Cequel::Type[:uuid])
|
207
|
-
end
|
208
|
-
|
209
|
-
it 'should read index attributes' do
|
210
|
-
expect(table.data_columns.find { |column| column.name == :author_id }.index_name).
|
211
|
-
to eq(:posts_author_id_idx)
|
212
|
-
end
|
213
|
-
|
214
|
-
it 'should leave nil index for non-indexed columns' do
|
215
|
-
expect(table.data_columns.find { |column| column.name == :title }.index_name).
|
216
|
-
to be_nil
|
217
|
-
end
|
218
|
-
|
219
|
-
it 'should read list columns' do
|
220
|
-
expect(table.data_columns.find { |column| column.name == :categories }).
|
221
|
-
to be_a(Cequel::Schema::List)
|
222
|
-
end
|
223
|
-
|
224
|
-
it 'should read list column type' do
|
225
|
-
expect(table.data_columns.find { |column| column.name == :categories }.type).
|
226
|
-
to eq(Cequel::Type[:text])
|
227
|
-
end
|
228
|
-
|
229
|
-
it 'should read set columns' do
|
230
|
-
expect(table.data_columns.find { |column| column.name == :tags }).
|
231
|
-
to be_a(Cequel::Schema::Set)
|
232
|
-
end
|
233
|
-
|
234
|
-
it 'should read set column type' do
|
235
|
-
expect(table.data_columns.find { |column| column.name == :tags }.type).
|
236
|
-
to eq(Cequel::Type[:text])
|
237
|
-
end
|
238
|
-
|
239
|
-
it 'should read map columns' do
|
240
|
-
expect(table.data_columns.find { |column| column.name == :trackbacks }).
|
241
|
-
to be_a(Cequel::Schema::Map)
|
242
|
-
end
|
243
|
-
|
244
|
-
it 'should read map column key type' do
|
245
|
-
expect(table.data_columns.find { |column| column.name == :trackbacks }.key_type).
|
246
|
-
to eq(Cequel::Type[:timestamp])
|
247
|
-
end
|
248
|
-
|
249
|
-
it 'should read map column value type' do
|
250
|
-
expect(table.data_columns.find { |column| column.name == :trackbacks }.
|
251
|
-
value_type).to eq(Cequel::Type[:ascii])
|
252
|
-
end
|
253
|
-
|
254
|
-
end # describe 'reading data columns'
|
255
|
-
|
256
|
-
describe 'reading storage properties' do
|
257
|
-
|
258
|
-
before do
|
259
|
-
cequel.execute <<-CQL
|
260
|
-
CREATE TABLE #{table_name} (permalink text PRIMARY KEY)
|
261
|
-
WITH bloom_filter_fp_chance = 0.02
|
262
|
-
AND comment = 'Posts table'
|
263
|
-
AND compaction = {
|
264
|
-
'class' : 'SizeTieredCompactionStrategy',
|
265
|
-
'bucket_high' : 1.8,
|
266
|
-
'max_threshold' : 64,
|
267
|
-
'min_sstable_size' : 50,
|
268
|
-
'tombstone_compaction_interval' : 2
|
269
|
-
} AND compression = {
|
270
|
-
'sstable_compression' : 'DeflateCompressor',
|
271
|
-
'chunk_length_kb' : 128,
|
272
|
-
'crc_check_chance' : 0.5
|
273
|
-
}
|
274
|
-
CQL
|
275
|
-
end
|
276
|
-
|
277
|
-
it 'should read float properties' do
|
278
|
-
expect(table.property(:bloom_filter_fp_chance)).to eq(0.02)
|
279
|
-
end
|
280
|
-
|
281
|
-
it 'should read string properties' do
|
282
|
-
expect(table.property(:comment)).to eq('Posts table')
|
283
|
-
end
|
284
|
-
|
285
|
-
it 'should read and simplify compaction class' do
|
286
|
-
expect(table.property(:compaction)[:class]).
|
287
|
-
to eq('SizeTieredCompactionStrategy')
|
288
|
-
end
|
289
|
-
|
290
|
-
it 'should read float properties from compaction hash' do
|
291
|
-
expect(table.property(:compaction)[:bucket_high]).to eq(1.8)
|
292
|
-
end
|
293
|
-
|
294
|
-
it 'should read integer properties from compaction hash' do
|
295
|
-
expect(table.property(:compaction)[:max_threshold]).to eq(64)
|
296
|
-
end
|
297
|
-
|
298
|
-
it 'should read and simplify compression class' do
|
299
|
-
expect(table.property(:compression)[:sstable_compression] ||
|
300
|
-
table.property(:compression)[:class]).
|
301
|
-
to eq('DeflateCompressor')
|
302
|
-
|
303
|
-
end
|
304
|
-
|
305
|
-
it 'should read integer properties from compression class' do
|
306
|
-
expect(table.property(:compression)[:chunk_length_kb]).to eq(128)
|
307
|
-
end
|
308
|
-
|
309
|
-
it 'should read float properties from compression class' do
|
310
|
-
expect(table.property(:compression)[:crc_check_chance]).to eq(0.5)
|
311
|
-
end
|
312
|
-
|
313
|
-
it 'should recognize no compact storage' do
|
314
|
-
expect(table).not_to be_compact_storage
|
315
|
-
end
|
316
|
-
|
317
|
-
end # describe 'reading storage properties'
|
318
|
-
|
319
|
-
describe 'skinny-row compact storage' do
|
320
|
-
before do
|
321
|
-
cequel.execute <<-CQL
|
322
|
-
CREATE TABLE #{table_name} (permalink text PRIMARY KEY, title text, body text)
|
323
|
-
WITH COMPACT STORAGE
|
324
|
-
CQL
|
325
|
-
end
|
326
|
-
subject { table }
|
327
|
-
|
328
|
-
it { is_expected.to be_compact_storage }
|
329
|
-
its(:partition_key_columns) { should ==
|
330
|
-
[Cequel::Schema::PartitionKey.new(:permalink, :text)] }
|
331
|
-
its(:clustering_columns) { should be_empty }
|
332
|
-
specify { expect(table.data_columns).to contain_exactly(
|
333
|
-
Cequel::Schema::DataColumn.new(:title, :text),
|
334
|
-
Cequel::Schema::DataColumn.new(:body, :text)) }
|
335
|
-
end
|
336
|
-
|
337
|
-
describe 'wide-row compact storage' do
|
338
|
-
before do
|
339
|
-
cequel.execute <<-CQL
|
340
|
-
CREATE TABLE #{table_name} (
|
341
|
-
blog_subdomain text,
|
342
|
-
id uuid,
|
343
|
-
data text,
|
344
|
-
PRIMARY KEY (blog_subdomain, id)
|
345
|
-
)
|
346
|
-
WITH COMPACT STORAGE
|
347
|
-
CQL
|
348
|
-
end
|
349
|
-
subject { table }
|
350
|
-
|
351
|
-
it { is_expected.to be_compact_storage }
|
352
|
-
its(:partition_key_columns) { should ==
|
353
|
-
[Cequel::Schema::PartitionKey.new(:blog_subdomain, :text)] }
|
354
|
-
its(:clustering_columns) { should ==
|
355
|
-
[Cequel::Schema::ClusteringColumn.new(:id, :uuid)] }
|
356
|
-
its(:data_columns) { should ==
|
357
|
-
[Cequel::Schema::DataColumn.new(:data, :text)] }
|
358
|
-
end
|
359
|
-
|
360
|
-
describe 'skinny-row legacy table', thrift: true do
|
361
|
-
before do
|
362
|
-
legacy_connection.execute <<-CQL
|
363
|
-
CREATE TABLE #{table_name} (permalink text PRIMARY KEY, title text, body text)
|
364
|
-
CQL
|
365
|
-
end
|
366
|
-
subject { table }
|
367
|
-
|
368
|
-
it { is_expected.to be_compact_storage }
|
369
|
-
its(:partition_key_columns) { is_expected.to eq(
|
370
|
-
[Cequel::Schema::PartitionKey.new(:permalink, :text)]
|
371
|
-
) }
|
372
|
-
its(:clustering_columns) { is_expected.to be_empty }
|
373
|
-
its(:data_columns) { is_expected.to match_array(
|
374
|
-
[Cequel::Schema::DataColumn.new(:title, :text),
|
375
|
-
Cequel::Schema::DataColumn.new(:body, :text)]
|
376
|
-
) }
|
377
445
|
end
|
378
446
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
CQL2
|
385
|
-
end
|
386
|
-
subject { table }
|
387
|
-
|
388
|
-
it { is_expected.to be_compact_storage }
|
389
|
-
its(:partition_key_columns) { is_expected.to eq(
|
390
|
-
[Cequel::Schema::PartitionKey.new(:blog_subdomain, :text)]
|
391
|
-
) }
|
392
|
-
its(:clustering_columns) { is_expected.to eq(
|
393
|
-
[Cequel::Schema::ClusteringColumn.new(:column1, :uuid)]
|
394
|
-
) }
|
395
|
-
its(:data_columns) { is_expected.to eq(
|
396
|
-
[Cequel::Schema::DataColumn.new(:value, :text)]
|
397
|
-
) }
|
447
|
+
def fetch_table_data(name=table_name)
|
448
|
+
cequel.send(:cluster).refresh_schema
|
449
|
+
cequel.send(:cluster)
|
450
|
+
.keyspace(cequel.name.to_s)
|
451
|
+
.table(name.to_s)
|
398
452
|
end
|
399
453
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
CREATE TABLE #{table_name} (
|
406
|
-
blog_subdomain text,
|
407
|
-
permalink ascii,
|
408
|
-
PRIMARY KEY (blog_subdomain, permalink)
|
409
|
-
)
|
410
|
-
CQL
|
411
|
-
cequel.execute <<-CQL
|
412
|
-
CREATE MATERIALIZED VIEW #{view_name} AS
|
413
|
-
SELECT blog_subdomain, permalink
|
414
|
-
FROM #{name}
|
415
|
-
WHERE blog_subdomain IS NOT NULL AND permalink IS NOT NULL
|
416
|
-
PRIMARY KEY ( blog_subdomain, permalink )
|
417
|
-
CQL
|
418
|
-
end
|
419
|
-
after do
|
420
|
-
cequel.schema.drop_materialized_view(view_name)
|
421
|
-
end
|
422
|
-
|
423
|
-
context 'when materialized_view' do
|
424
|
-
let(:reader) { cequel.schema.get_table_reader(view_name) }
|
425
|
-
subject { reader }
|
426
|
-
its(:materialized_view?) { should eq true }
|
427
|
-
end
|
428
|
-
|
429
|
-
context 'when table' do
|
430
|
-
let(:reader) { cequel.schema.get_table_reader(name) }
|
431
|
-
subject { reader }
|
432
|
-
its(:materialized_view?) { should eq false }
|
433
|
-
end
|
454
|
+
def fetch_view_data(name=view_name)
|
455
|
+
cequel.send(:cluster).refresh_schema
|
456
|
+
cequel.send(:cluster)
|
457
|
+
.keyspace(cequel.name.to_s)
|
458
|
+
.materialized_view(name.to_s)
|
434
459
|
end
|
435
460
|
end
|