cequel 2.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/cequel/schema/table.rb
CHANGED
@@ -12,19 +12,25 @@ module Cequel
|
|
12
12
|
|
13
13
|
# @return [Symbol] the name of the table
|
14
14
|
attr_reader :name
|
15
|
+
|
15
16
|
# @return [Array<Column>] all columns defined on the table
|
16
17
|
attr_reader :columns
|
18
|
+
|
17
19
|
# @return [Array<PartitionKey>] partition key columns defined on the
|
18
20
|
# table
|
19
21
|
attr_reader :partition_key_columns
|
22
|
+
|
20
23
|
# @return [Array<ClusteringColumn>] clustering columns defined on the
|
21
24
|
# table
|
22
25
|
attr_reader :clustering_columns
|
26
|
+
|
23
27
|
# @return [Array<DataColumn,CollectionColumn>] data columns and
|
24
28
|
# collection columns defined on the table
|
25
29
|
attr_reader :data_columns
|
30
|
+
|
26
31
|
# @return [Hash] storage properties defined on the table
|
27
32
|
attr_reader :properties
|
33
|
+
|
28
34
|
# @return [Boolean] `true` if this table is configured with compact
|
29
35
|
# storage
|
30
36
|
attr_writer :compact_storage
|
@@ -33,147 +39,48 @@ module Cequel
|
|
33
39
|
# @param name [Symbol] the name of the table
|
34
40
|
# @api private
|
35
41
|
#
|
36
|
-
def initialize(name)
|
37
|
-
@name = name
|
42
|
+
def initialize(name, is_view=false)
|
43
|
+
@name = name.to_sym
|
44
|
+
@is_view = is_view
|
38
45
|
@partition_key_columns, @clustering_columns, @data_columns = [], [], []
|
39
46
|
@columns, @columns_by_name = [], {}
|
40
47
|
@properties = ActiveSupport::HashWithIndifferentAccess.new
|
41
48
|
end
|
42
49
|
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
# @param name [Symbol] the name of the column
|
48
|
-
# @param type [Symbol,Type] the type for the column
|
49
|
-
# @param clustering_order [:asc,:desc] whether rows should be in
|
50
|
-
# ascending or descending order by this column. Only meaningful for
|
51
|
-
# clustering columns.
|
52
|
-
# @return [void]
|
53
|
-
#
|
54
|
-
# @see #add_partition_key
|
55
|
-
#
|
56
|
-
def add_key(name, type, clustering_order = nil)
|
57
|
-
if @partition_key_columns.empty?
|
58
|
-
unless clustering_order.nil?
|
59
|
-
fail ArgumentError,
|
60
|
-
"Can't set clustering order for partition key #{name}"
|
61
|
-
end
|
62
|
-
add_partition_key(name, type)
|
63
|
-
else
|
64
|
-
add_clustering_column(name, type, clustering_order)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
#
|
69
|
-
# Define a partition key for the table
|
70
|
-
#
|
71
|
-
# @param name [Symbol] the name of the column
|
72
|
-
# @param type [Symbol,Type] the type for the column
|
73
|
-
# @return [void]
|
74
|
-
#
|
75
|
-
def add_partition_key(name, type)
|
76
|
-
PartitionKey.new(name, type(type)).tap do |column|
|
77
|
-
@partition_key_columns << add_column(column)
|
78
|
-
end
|
50
|
+
# @return [Boolean] `true` when this table is a materialized view
|
51
|
+
def materialized_view?
|
52
|
+
@is_view
|
79
53
|
end
|
80
54
|
|
55
|
+
# Add a column descriptor to this table descriptor.
|
81
56
|
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
# @param (see #add_key)
|
85
|
-
# @return [void]
|
57
|
+
# column_desc - Descriptor of column to add. Can be PartitionKey,
|
58
|
+
# ClusteringColumn, DataColumn, List, Set, or Map.
|
86
59
|
#
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
60
|
+
def add_column(column_desc)
|
61
|
+
column_flavor = case column_desc
|
62
|
+
when PartitionKey
|
63
|
+
@partition_key_columns
|
64
|
+
when ClusteringColumn
|
65
|
+
@clustering_columns
|
66
|
+
else
|
67
|
+
@data_columns
|
68
|
+
end
|
91
69
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# @param name [Symbol] name of the column
|
96
|
-
# @param type [Type] type for the column
|
97
|
-
# @param options [Options] options for the column
|
98
|
-
# @option options [Boolean,Symbol] :index (nil) name of a secondary index
|
99
|
-
# to apply to the column, or `true` to infer an index name by
|
100
|
-
# convention
|
101
|
-
# @return [void]
|
102
|
-
#
|
103
|
-
def add_data_column(name, type, options = {})
|
104
|
-
options = {index: options} unless options.is_a?(Hash)
|
105
|
-
index_name = options[:index]
|
106
|
-
index_name = :"#{@name}_#{name}_idx" if index_name == true
|
107
|
-
if type == :enum
|
108
|
-
type = :int
|
109
|
-
end
|
110
|
-
DataColumn.new(name, type(type), index_name)
|
111
|
-
.tap { |column| @data_columns << add_column(column) }
|
70
|
+
column_flavor << column_desc
|
71
|
+
columns << column_desc
|
72
|
+
columns_by_name[column_desc.name] = column_desc
|
112
73
|
end
|
113
74
|
|
75
|
+
# Add a property to this table descriptor
|
114
76
|
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
# @param name [Symbol] name of the list
|
118
|
-
# @param type [Symbol,Type] type of the list's elements
|
119
|
-
# @return [void]
|
120
|
-
#
|
121
|
-
# @see List
|
77
|
+
# property_desc - A `TableProperty` describing one property of this table.
|
122
78
|
#
|
123
|
-
def
|
124
|
-
|
125
|
-
@data_columns << add_column(column)
|
126
|
-
end
|
79
|
+
def add_property(property_desc)
|
80
|
+
properties[property_desc.name] = property_desc
|
127
81
|
end
|
128
82
|
|
129
83
|
#
|
130
|
-
# Define a set column on the table
|
131
|
-
#
|
132
|
-
# @param name [Symbol] name of the set
|
133
|
-
# @param type [Symbol,Type] type of the set's elements
|
134
|
-
# @return [void]
|
135
|
-
#
|
136
|
-
# @see Set
|
137
|
-
#
|
138
|
-
def add_set(name, type)
|
139
|
-
Set.new(name, type(type)).tap do |column|
|
140
|
-
@data_columns << add_column(column)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
#
|
145
|
-
# Define a map column on the table
|
146
|
-
#
|
147
|
-
# @param name [Symbol] name of the set
|
148
|
-
# @param key_type [Symbol,Type] type of the map's keys
|
149
|
-
# @param value_type [Symbol,Type] type of the map's values
|
150
|
-
# @return [void]
|
151
|
-
#
|
152
|
-
# @see Map
|
153
|
-
#
|
154
|
-
def add_map(name, key_type, value_type)
|
155
|
-
Map.new(name, type(key_type), type(value_type)).tap do |column|
|
156
|
-
@data_columns << add_column(column)
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
#
|
161
|
-
# Define a storage property for the table
|
162
|
-
#
|
163
|
-
# @param name [Symbol] name of the property
|
164
|
-
# @param value value for the property
|
165
|
-
# @return [void]
|
166
|
-
#
|
167
|
-
# @see STORAGE_PROPERTIES List of storage property names
|
168
|
-
# @see http://cassandra.apache.org/doc/cql3/CQL.html#createTableOptions
|
169
|
-
# list of CQL3 table storage properties
|
170
|
-
#
|
171
|
-
def add_property(name, value)
|
172
|
-
TableProperty.build(name, value).tap do |property|
|
173
|
-
@properties[name] = property
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
84
|
#
|
178
85
|
# @param name [Symbol] name of column to look up
|
179
86
|
# @return [Column] column defined on table with given name
|
@@ -182,17 +89,23 @@ module Cequel
|
|
182
89
|
columns_by_name[name.to_sym]
|
183
90
|
end
|
184
91
|
|
92
|
+
# Returns true iff this table has the specified column name.
|
93
|
+
#
|
94
|
+
def has_column?(name)
|
95
|
+
columns_by_name.has_key?(name.to_sym)
|
96
|
+
end
|
97
|
+
|
185
98
|
#
|
186
99
|
# @return [Array<Symbol>] the names of all columns
|
187
100
|
def column_names
|
188
|
-
|
101
|
+
columns_by_name.keys
|
189
102
|
end
|
190
103
|
|
191
104
|
#
|
192
105
|
# @return [Array<Column>] all key columns (partition + clustering)
|
193
106
|
#
|
194
107
|
def key_columns
|
195
|
-
|
108
|
+
partition_key_columns + clustering_columns
|
196
109
|
end
|
197
110
|
|
198
111
|
#
|
@@ -224,6 +137,12 @@ module Cequel
|
|
224
137
|
partition_key_columns.length
|
225
138
|
end
|
226
139
|
|
140
|
+
# Returns true iff this table descriptor currently has at least one
|
141
|
+
# partition key defined.
|
142
|
+
def has_partition_key?
|
143
|
+
partition_key_columns.any?
|
144
|
+
end
|
145
|
+
|
227
146
|
#
|
228
147
|
# @return [Array<Symbol>] names of clustering columns
|
229
148
|
#
|
@@ -243,7 +162,7 @@ module Cequel
|
|
243
162
|
# @return [PartitionKey] partition key column with given name
|
244
163
|
#
|
245
164
|
def partition_key(name)
|
246
|
-
|
165
|
+
partition_key_columns.find { |column| column.name == name }
|
247
166
|
end
|
248
167
|
|
249
168
|
#
|
@@ -251,7 +170,7 @@ module Cequel
|
|
251
170
|
# @return [ClusteringColumn] clustering column with given name
|
252
171
|
#
|
253
172
|
def clustering_column(name)
|
254
|
-
|
173
|
+
clustering_columns.find { |column| column.name == name }
|
255
174
|
end
|
256
175
|
|
257
176
|
#
|
@@ -261,7 +180,7 @@ module Cequel
|
|
261
180
|
#
|
262
181
|
def data_column(name)
|
263
182
|
name = name.to_sym
|
264
|
-
|
183
|
+
data_columns.find { |column| column.name == name }
|
265
184
|
end
|
266
185
|
|
267
186
|
#
|
@@ -269,7 +188,7 @@ module Cequel
|
|
269
188
|
# @return [TableProperty] property as defined on table
|
270
189
|
#
|
271
190
|
def property(name)
|
272
|
-
|
191
|
+
properties.fetch(name, null_table_property).value
|
273
192
|
end
|
274
193
|
|
275
194
|
#
|
@@ -283,19 +202,18 @@ module Cequel
|
|
283
202
|
|
284
203
|
attr_reader :columns_by_name
|
285
204
|
|
286
|
-
private
|
287
|
-
|
288
|
-
def add_column(column)
|
289
|
-
columns << column
|
290
|
-
columns_by_name[column.name] = column
|
291
|
-
end
|
292
|
-
|
293
205
|
def type(type)
|
294
206
|
type = type.kind if type.respond_to?(:kind)
|
295
207
|
|
296
208
|
::Cequel::Type[type]
|
297
209
|
end
|
298
210
|
|
211
|
+
def null_table_property
|
212
|
+
@@null_table_property ||= Class.new do
|
213
|
+
def value; nil; end
|
214
|
+
def name; nil; end
|
215
|
+
end.new
|
216
|
+
end
|
299
217
|
end
|
300
218
|
end
|
301
219
|
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Cequel
|
3
|
+
module Schema
|
4
|
+
#
|
5
|
+
# Implements a DSL used to describe CQL tables.
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# TableDescDsl.new("posts").eval do
|
10
|
+
# partition_key :blog_subdomain, :text
|
11
|
+
# key :slug, :text
|
12
|
+
# column :body, :text
|
13
|
+
# set :author_names, :text
|
14
|
+
# list :comments, :text
|
15
|
+
# map :something_contrived, :text, :text
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# TableDescDsl.new("posts_view").eval do
|
19
|
+
# materialized_view
|
20
|
+
# partition_key :blog_subdomain, :text
|
21
|
+
# key :slug, :text
|
22
|
+
# column :body, :text
|
23
|
+
# end
|
24
|
+
|
25
|
+
#
|
26
|
+
class TableDescDsl < BasicObject
|
27
|
+
extend ::Cequel::Util::Forwardable
|
28
|
+
|
29
|
+
# Initialize a new instance
|
30
|
+
#
|
31
|
+
# table_name - The name of the table being described.
|
32
|
+
protected def initialize(table_name)
|
33
|
+
@table_name = table_name
|
34
|
+
@columns = []
|
35
|
+
@properties = []
|
36
|
+
@is_compact_storage = false
|
37
|
+
@is_view = false
|
38
|
+
@has_part_key = false
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a Table object built by evaluating the provided block.
|
42
|
+
#
|
43
|
+
# Yields nothing but block is instance_evaled so it as access to
|
44
|
+
# all the methods of the instance.
|
45
|
+
def eval(&desc_block)
|
46
|
+
instance_eval(&desc_block)
|
47
|
+
|
48
|
+
table
|
49
|
+
end
|
50
|
+
|
51
|
+
# Describe (one of) the partition key(s) of the table.
|
52
|
+
#
|
53
|
+
# name - The name of the column.
|
54
|
+
# type - The type of the column. Either a `Cequel::Type` or a symbol.
|
55
|
+
# See `Cequel::Type`.
|
56
|
+
#
|
57
|
+
def partition_key(name, type)
|
58
|
+
columns << PartitionKey.new(name, type(type))
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# Describe (one of) the key(s) of the table.
|
63
|
+
#
|
64
|
+
# name - The name of the column
|
65
|
+
# type - The type of the column. Either a `Cequel::Type` or a symbol.
|
66
|
+
# See `Cequel::Type`.
|
67
|
+
# clustering_order - `:asc` or `:desc`. Only meaningful for cluster
|
68
|
+
# keys. Leave nil for partition keys.
|
69
|
+
#
|
70
|
+
def key(name, type, clustering_order = nil)
|
71
|
+
columns << if has_partition_key?
|
72
|
+
ClusteringColumn.new(name, type(type), clustering_order)
|
73
|
+
else
|
74
|
+
(fail ArgumentError, "Can't set clustering order for partition key #{name}") if clustering_order
|
75
|
+
|
76
|
+
PartitionKey.new(name, type(type))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Describe a column of the table
|
81
|
+
#
|
82
|
+
# name - The name of the column.
|
83
|
+
# type - The type of the column. Either a `Cequel::Type` or a symbol.
|
84
|
+
# See `Cequel::Type`.
|
85
|
+
# options
|
86
|
+
# :index - name of a secondary index to apply to the column, or
|
87
|
+
# `true` to infer an index name by convention
|
88
|
+
#
|
89
|
+
def column(name, type, options = {})
|
90
|
+
columns << DataColumn.new(name, type(type),
|
91
|
+
figure_index_name(name, options.fetch(:index, nil)))
|
92
|
+
end
|
93
|
+
|
94
|
+
# Describe a column of type list.
|
95
|
+
#
|
96
|
+
# name - The name of the column.
|
97
|
+
# type - The type of the elements of this column. Either a
|
98
|
+
# `Cequel::Type` or a symbol. See `Cequel::Type`.
|
99
|
+
#
|
100
|
+
def list(name, type)
|
101
|
+
columns << List.new(name, type(type))
|
102
|
+
end
|
103
|
+
|
104
|
+
# Describe a column of type set.
|
105
|
+
#
|
106
|
+
# name - The name of the column.
|
107
|
+
# type - The type of the members of this column. Either a
|
108
|
+
# `Cequel::Type` or a symbol. See `Cequel::Type`.
|
109
|
+
#
|
110
|
+
def set(name, type)
|
111
|
+
columns << Set.new(name, type(type))
|
112
|
+
end
|
113
|
+
|
114
|
+
# Describe a column of type map.
|
115
|
+
#
|
116
|
+
# name - The name of the column.
|
117
|
+
# key_type - The type of the keys of this column. Either a
|
118
|
+
# `Cequel::Type` or a symbol. See `Cequel::Type`.
|
119
|
+
# value_type - The type of the values of this column. Either a
|
120
|
+
# `Cequel::Type` or a symbol. See `Cequel::Type`.
|
121
|
+
def map(name, key_type, value_type)
|
122
|
+
columns << Map.new(name, type(key_type), type(value_type))
|
123
|
+
end
|
124
|
+
|
125
|
+
# Describe property of the table.
|
126
|
+
#
|
127
|
+
# name - name of property.
|
128
|
+
# value - value of property.
|
129
|
+
#
|
130
|
+
# See `STORAGE_PROPERTIES` List of storage property names
|
131
|
+
# See http://cassandra.apache.org/doc/cql3/CQL.html#createTableOptions
|
132
|
+
# list of CQL3 table storage properties
|
133
|
+
#
|
134
|
+
def with(name, value)
|
135
|
+
properties << TableProperty.build(name, value)
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Direct that this table use "compact storage". This is primarily useful
|
140
|
+
# for backwards compatibility with legacy CQL2 table schemas.
|
141
|
+
#
|
142
|
+
# @return [void]
|
143
|
+
#
|
144
|
+
def compact_storage
|
145
|
+
@is_compact_storage = true
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# Indicates that this is a materialized view.
|
150
|
+
#
|
151
|
+
# @return [void]
|
152
|
+
def materialized_view
|
153
|
+
self.is_view = true
|
154
|
+
end
|
155
|
+
|
156
|
+
def table
|
157
|
+
Table.new(table_name, is_view).tap do |tab|
|
158
|
+
columns.each do |c|
|
159
|
+
tab.add_column c
|
160
|
+
end
|
161
|
+
properties.each do |p|
|
162
|
+
tab.add_property p
|
163
|
+
end
|
164
|
+
tab.compact_storage = is_compact_storage
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
protected
|
169
|
+
|
170
|
+
attr_reader :table_name, :columns, :properties, :is_compact_storage,
|
171
|
+
:is_view
|
172
|
+
|
173
|
+
|
174
|
+
def has_partition_key?
|
175
|
+
columns.any?{|c| c.partition_key? }
|
176
|
+
end
|
177
|
+
|
178
|
+
def type(type)
|
179
|
+
type = :int if type == :enum
|
180
|
+
|
181
|
+
::Cequel::Type[type]
|
182
|
+
end
|
183
|
+
|
184
|
+
def figure_index_name(column_name, idx_opt)
|
185
|
+
case idx_opt
|
186
|
+
when true
|
187
|
+
:"#{table_name}_#{column_name}_idx"
|
188
|
+
when false, nil
|
189
|
+
nil
|
190
|
+
else
|
191
|
+
idx_opt
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|