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
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
|