cequel 2.0.2 → 2.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e428934f7aef3a261e5ee1c06b940ea008fd17de
4
- data.tar.gz: 49a65b153251cb761a8421c56268d5b9c83d6966
3
+ metadata.gz: 2ff9eed6cd9463e5896643152fd7314b4df3324e
4
+ data.tar.gz: 6e62173d98fa94f77d6022ca22501ae778e40139
5
5
  SHA512:
6
- metadata.gz: 623e015fa5d288ca1302eafaec1ada9aa0224b23a28c854e6e69e3dd7dfaa6d5e32a768291ecb9d23d2b3eb4f80a272fe57ef045cb4651f5a69217a264cafb3e
7
- data.tar.gz: 5ddbd20b44c69675be0025cea406961dad9a336b91853ae68b30914138e4b78c23baff03be79278a387b24d0a827e676485db78c045d5d5e9ffc40adcf902ed3
6
+ metadata.gz: 4ad422adb85827d83efa126565c8d43f4c1b34626a154e2f627fcdb7d04e0567d6635941cc88bfa622a3f79aeec875fd30da653ecbe9c71f0f9a45a1bc738c1b
7
+ data.tar.gz: 1d157437a9c7d435412a22206d8e2103b2b214c494acdd2719a3aae8d7ef5969dd674036b271f823bd026aa50b664afd99413afc5b4edb0bfd61cb2f249f773c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 2.0.3
2
+
3
+ * Add synchronization around use of @cluster and other variables Fix ([PR 333](https://github.com/cequel/cequel/pull/333))
4
+ * expose if the dataset is on the last page ([PR 335](https://github.com/cequel/cequel/pull/335))
5
+ * Delegate error handling to a policy object, allow for arbitrary options to be passed to cassandra driver gem ([PR 336](https://github.com/cequel/cequel/pull/336))
6
+ * Fixes README.md ([PR 340](https://github.com/cequel/cequel/pull/340))
7
+ * skip synchronizing materialized view ([PR 346](https://github.com/cequel/cequel/pull/346))
8
+ * Fixed link to cassandra documentation ([PR 347](https://github.com/cequel/cequel/pull/347))
9
+
10
+
1
11
  ## 2.0.2
2
12
 
3
13
  * Fix intermittent failures around preparing statements ([PR 330](https://github.com/cequel/cequel/pull/330))
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cequel (2.0.2)
4
+ cequel (2.0.3)
5
5
  activemodel (>= 4.0)
6
6
  cassandra-driver (~> 3.0)
7
7
 
data/README.md CHANGED
@@ -231,7 +231,7 @@ multiple queries in your logs if you're iterating over a huge result set.
231
231
  CQL has [special handling for the `timeuuid`
232
232
  type](https://docs.datastax.com/en/cql/3.3/cql/cql_reference/uuid_type_r.html),
233
233
  which allows you to return a rows whose UUID keys correspond to a range of
234
- timestamps.
234
+ timestamps.
235
235
 
236
236
  Cequel automatically constructs timeuuid range queries if you pass a `Time`
237
237
  value for a range over a `timeuuid` column. So, if you want to get the posts
@@ -393,7 +393,7 @@ Both read and write consistency default to `QUORUM`.
393
393
  ### Compression ###
394
394
 
395
395
  Cassandra supports [frame compression](http://datastax.github.io/ruby-driver/features/#compression),
396
- which can give you a performance boost if your requests or responses are big. To enable it you can
396
+ which can give you a performance boost if your requests or responses are big. To enable it you can
397
397
  specify `client_compression` to use in cequel.yaml.
398
398
 
399
399
  ```yaml
@@ -539,14 +539,6 @@ essentially the same thing: both simply persist the given column data at the
539
539
  given key(s). So, you may think you are creating a new record, but in fact
540
540
  you're overwriting data at an existing record:
541
541
 
542
- #### Counting ####
543
-
544
- Counting is not the same as in a RDB, as it can have a much longer runtime and
545
- can put unexpected load on your cluster. As a result Cequel does not support
546
- this feature. It is still possible to execute raw cql to get the counts, should
547
- you require this functionality.
548
- `MyModel.connection.execute('select count(*) from table_name;').first['count']`
549
-
550
542
  ``` ruby
551
543
  # I'm just creating a blog here.
552
544
  blog1 = Blog.create!(
@@ -566,6 +558,14 @@ above code will just overwrite the `name` in that row. Note that the
566
558
  `description` will not be touched by the second statement; upserts only work on
567
559
  the columns that are given.
568
560
 
561
+ #### Counting ####
562
+
563
+ Counting is not the same as in a RDB, as it can have a much longer runtime and
564
+ can put unexpected load on your cluster. As a result Cequel does not support
565
+ this feature. It is still possible to execute raw cql to get the counts, should
566
+ you require this functionality.
567
+ `MyModel.connection.execute('select count(*) from table_name;').first['count']`
568
+
569
569
  ## Compatibility ##
570
570
 
571
571
  ### Rails ###
data/lib/cequel.rb CHANGED
@@ -8,6 +8,7 @@ require 'cassandra'
8
8
 
9
9
  require 'cequel/errors'
10
10
  require 'cequel/util'
11
+ require 'cequel/metal/policy/cassandra_error'
11
12
  require 'cequel/metal'
12
13
  require 'cequel/schema'
13
14
  require 'cequel/type'
@@ -579,10 +579,26 @@ module Cequel
579
579
  end
580
580
  end
581
581
 
582
+ #
583
+ # Exposes current paging state for stateless pagination
584
+ #
585
+ # @return [String] or nil
586
+ #
587
+ # @see http://docs.datastax.com/en/developer/ruby-driver/3.0/api/cassandra/result/#paging_state-instance_method
588
+ #
582
589
  def next_paging_state
583
590
  results.paging_state
584
591
  end
585
592
 
593
+ #
594
+ # @return [Boolean] Returns whether no more pages are available
595
+ #
596
+ # @see http://docs.datastax.com/en/developer/ruby-driver/3.0/api/cassandra/result/#last_page?-instance_method
597
+ #
598
+ def last_page?
599
+ results.last_page?
600
+ end
601
+
586
602
  # rubocop:enable LineLength
587
603
 
588
604
  #
@@ -21,10 +21,6 @@ module Cequel
21
21
  attr_reader :hosts
22
22
  # @return Integer port to connect to Cassandra nodes on
23
23
  attr_reader :port
24
- # @return Integer maximum number of retries to reconnect to Cassandra
25
- attr_reader :max_retries
26
- # @return Float delay between retries to reconnect to Cassandra
27
- attr_reader :retry_delay
28
24
  # @return [Symbol] the default consistency for queries in this keyspace
29
25
  # @since 1.1.0
30
26
  attr_writer :default_consistency
@@ -34,6 +30,10 @@ module Cequel
34
30
  attr_reader :ssl_config
35
31
  # @return [Symbol] The client compression option
36
32
  attr_reader :client_compression
33
+ # @return [Hash] A hash of additional options passed to Cassandra, if any
34
+ attr_reader :cassandra_options
35
+ # @return [Object] The error policy object in use by this keyspace
36
+ attr_reader :error_policy
37
37
 
38
38
  #
39
39
  # @!method write(statement, *bind_vars)
@@ -52,7 +52,7 @@ module Cequel
52
52
  # consistency. Will be included the current batch operation if one is
53
53
  # present.
54
54
  #
55
- # @param (see #execute_with_consistency)
55
+ # @param (see #execute_with_options)
56
56
  # @return [void]
57
57
  #
58
58
  def_delegator :write_target, :execute_with_options,
@@ -94,8 +94,8 @@ module Cequel
94
94
  # @see Cequel.connect
95
95
  #
96
96
  def initialize(configuration={})
97
- configure(configuration)
98
97
  @lock = Monitor.new
98
+ configure(configuration)
99
99
  end
100
100
 
101
101
  #
@@ -122,8 +122,12 @@ module Cequel
122
122
  # certificate
123
123
  # @option configuration [String] :private_key path to ssl client private
124
124
  # key
125
- # @option configuartion [String] :passphrase the passphrase for client
125
+ # @option configuration [String] :passphrase the passphrase for client
126
126
  # private key
127
+ # @option configuration [String] :cassandra_error_policy A mixin for
128
+ # handling errors from Cassandra
129
+ # @option configuration [Hash] :cassandra_options A hash of arbitrary
130
+ # options to pass to Cassandra
127
131
  # @return [void]
128
132
  #
129
133
  def configure(configuration = {})
@@ -132,11 +136,11 @@ module Cequel
132
136
  "with Cassandra. The :thrift option is deprecated and ignored."
133
137
  end
134
138
  @configuration = configuration
135
-
139
+
140
+ @error_policy = extract_cassandra_error_policy(configuration)
141
+ @cassandra_options = extract_cassandra_options(configuration)
136
142
  @hosts, @port = extract_hosts_and_port(configuration)
137
143
  @credentials = extract_credentials(configuration)
138
- @max_retries = extract_max_retries(configuration)
139
- @retry_delay = extract_retry_delay(configuration)
140
144
  @ssl_config = extract_ssl_config(configuration)
141
145
 
142
146
  @name = configuration[:keyspace]
@@ -184,7 +188,7 @@ module Cequel
184
188
  # @param bind_vars [Object] values for bind variables
185
189
  # @return [Enumerable] the results of the query
186
190
  #
187
- # @see #execute_with_consistency
191
+ # @see #execute_with_options
188
192
  #
189
193
  def execute(statement, *bind_vars)
190
194
  execute_with_options(Statement.new(statement, bind_vars), { consistency: default_consistency })
@@ -211,7 +215,7 @@ module Cequel
211
215
  end
212
216
 
213
217
  log('CQL', statement) do
214
- client_retry do
218
+ error_policy.execute_stmt(self) do
215
219
  client.execute(cql, options)
216
220
  end
217
221
  end
@@ -230,7 +234,7 @@ module Cequel
230
234
  else
231
235
  statement
232
236
  end
233
- client_retry do
237
+ error_policy.execute_stmt(self) do
234
238
  client.prepare(cql)
235
239
  end
236
240
  end
@@ -241,15 +245,17 @@ module Cequel
241
245
  # @return [void]
242
246
  #
243
247
  def clear_active_connections!
244
- if defined? @client
245
- remove_instance_variable(:@client)
246
- end
247
- if defined? @client_without_keyspace
248
- remove_instance_variable(:@client_without_keyspace)
249
- end
250
- if defined? @cluster
251
- @cluster.close
252
- remove_instance_variable(:@cluster)
248
+ synchronize do
249
+ if defined? @client
250
+ remove_instance_variable(:@client)
251
+ end
252
+ if defined? @client_without_keyspace
253
+ remove_instance_variable(:@client_without_keyspace)
254
+ end
255
+ if defined? @cluster
256
+ @cluster.close
257
+ remove_instance_variable(:@cluster)
258
+ end
253
259
  end
254
260
  end
255
261
 
@@ -308,21 +314,6 @@ module Cequel
308
314
  def_delegator :lock, :synchronize
309
315
  private :lock
310
316
 
311
- def client_retry
312
- retries = max_retries
313
- begin
314
- yield
315
- rescue Cassandra::Errors::NoHostsAvailable,
316
- Cassandra::Errors::ExecutionError,
317
- Cassandra::Errors::TimeoutError
318
- clear_active_connections!
319
- raise if retries == 0
320
- retries -= 1
321
- sleep(retry_delay)
322
- retry
323
- end
324
- end
325
-
326
317
  def client_without_keyspace
327
318
  synchronize do
328
319
  @client_without_keyspace ||= cluster.connect
@@ -334,6 +325,7 @@ module Cequel
334
325
  options.merge!(credentials) if credentials
335
326
  options.merge!(ssl_config) if ssl_config
336
327
  options.merge!(compression: client_compression) if client_compression
328
+ options.merge!(cassandra_options) if cassandra_options
337
329
  end
338
330
  end
339
331
 
@@ -373,14 +365,6 @@ module Cequel
373
365
  configuration.slice(:username, :password).presence
374
366
  end
375
367
 
376
- def extract_max_retries(configuration)
377
- configuration.fetch(:max_retries, 3)
378
- end
379
-
380
- def extract_retry_delay(configuration)
381
- configuration.fetch(:retry_delay, 0.5)
382
- end
383
-
384
368
  def extract_ssl_config(configuration)
385
369
  ssl_config = {}
386
370
  ssl_config[:ssl] = configuration.fetch(:ssl, nil)
@@ -391,7 +375,24 @@ module Cequel
391
375
  ssl_config.each { |key, value| ssl_config.delete(key) unless value }
392
376
  ssl_config
393
377
  end
394
-
378
+
379
+ def extract_cassandra_error_policy(configuration)
380
+ value = configuration.fetch(:cassandra_error_policy, ::Cequel::Metal::Policy::CassandraError::ClearAndRetryPolicy)
381
+ # Accept a class name as a string, create an instance of it
382
+ if value.is_a?(String)
383
+ value.constantize.new(configuration)
384
+ # Accept a class, instantiate it
385
+ elsif value.is_a?(Class)
386
+ value.new(configuration)
387
+ # Accept a value, assume it is a ready to use policy object
388
+ else
389
+ value
390
+ end
391
+ end
392
+
393
+ def extract_cassandra_options(configuration)
394
+ configuration[:cassandra_options]
395
+ end
395
396
  end
396
397
  end
397
398
  end
@@ -0,0 +1,75 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Cequel
3
+ module Metal
4
+ module Policy
5
+ module CassandraError
6
+ class ErrorPolicyBase
7
+ # This class is used by the Keyspace object to dictate
8
+ # how a failure from Cassandra is handled.
9
+ # The only method defined is
10
+ # execute_stmt(keyspace)
11
+ # The first argument is an instance of the Keyspace class
12
+ # This method may raise an error to abort the operation,
13
+ #
14
+ # The specific instance is chosen by passing configuration options
15
+ # See Keyspace#configure
16
+
17
+ # On instantiation, the configuraiton hash passed to Cequel is
18
+ # available here
19
+ def initialize(options = {})
20
+ end
21
+
22
+ def execute_stmt(keyspace)
23
+ raise NotImplementedError, "#execute_stmt must be implemented in #{self.class.name}"
24
+ end
25
+ end
26
+
27
+ class ClearAndRetryPolicy < ErrorPolicyBase
28
+ # @return Integer maximum number of retries to reconnect to Cassandra
29
+ attr_reader :max_retries
30
+ # @return Float delay between retries to reconnect to Cassandra
31
+ attr_reader :retry_delay
32
+ # @return Boolean if this policy clears connections before retry
33
+ attr_reader :clear_before_retry
34
+ def initialize(options = {})
35
+ @max_retries = options.fetch(:max_retries, 3)
36
+ @retry_delay = options.fetch(:retry_delay, 0.5)
37
+ @clear_before_retry = !!options.fetch(:clear_before_retry, true)
38
+
39
+ if @retry_delay <= 0.0
40
+ raise ArgumentError, "The value for retry must be a positive number, not '#{@retry_delay}'"
41
+ end
42
+ end
43
+
44
+ def execute_stmt(keyspace)
45
+ retries_remaining = max_retries
46
+ begin
47
+ yield
48
+ rescue Cassandra::Errors::NoHostsAvailable,
49
+ Cassandra::Errors::ExecutionError,
50
+ Cassandra::Errors::TimeoutError => exc
51
+ raise error if retries_remaining == 0
52
+ sleep(retry_delay)
53
+ keyspace.clear_active_connections! if clear_before_retry
54
+ retries_remaining -= 1
55
+ retry
56
+ end
57
+ end
58
+ end
59
+
60
+ module RetryPolicy
61
+ def self.new(options = {})
62
+ options[:clear_before_retry] = false
63
+ ClearAndRetryPolicy.new(options)
64
+ end
65
+ end
66
+
67
+ class RaisePolicy < ErrorPolicyBase
68
+ def execute_stmt(keyspace)
69
+ yield
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -173,7 +173,7 @@ module Cequel
173
173
  # @param options [Options] options for save
174
174
  # @option options [Boolean] :validate (true) whether to run validations
175
175
  # before saving
176
- # @option options [Symbol] :consistency (:quorum) what consistency with
176
+ # @option options [Symbol] :consistency what consistency with
177
177
  # which to persist the changes
178
178
  # @option options [Integer] :ttl time-to-live of the updated rows in
179
179
  # seconds
@@ -211,7 +211,7 @@ module Cequel
211
211
  # Remove this record from the database
212
212
  #
213
213
  # @param options [Options] options for deletion
214
- # @option options [Symbol] :consistency (:quorum) what consistency with
214
+ # @option options [Symbol] :consistency what consistency with
215
215
  # which to persist the deletion
216
216
  # @option options [Time] :timestamp the writetime to use for the deletion
217
217
  #
@@ -488,6 +488,10 @@ module Cequel
488
488
  data_set.next_paging_state
489
489
  end
490
490
 
491
+ def last_page?
492
+ data_set.last_page?
493
+ end
494
+
491
495
  #
492
496
  # @overload first
493
497
  # @return [Record] the first record in this record set
@@ -80,8 +80,21 @@ module Cequel
80
80
  # @return [void]
81
81
  #
82
82
  def synchronize_schema
83
+ reader = get_table_reader
84
+ return if reader.materialized_view?
83
85
  Cequel::Schema::TableSynchronizer
84
- .apply(connection, read_schema, table_schema)
86
+ .apply(connection, reader.read, table_schema)
87
+ end
88
+
89
+ #
90
+ # Return a {TableReader} instance
91
+ #
92
+ # @return [Schema::TableReader] object
93
+ #
94
+ def get_table_reader
95
+ fail MissingTableNameError unless table_name
96
+
97
+ connection.schema.get_table_reader(table_name)
85
98
  end
86
99
 
87
100
  #
@@ -92,9 +105,7 @@ module Cequel
92
105
  # table in the database
93
106
  #
94
107
  def read_schema
95
- fail MissingTableNameError unless table_name
96
-
97
- connection.schema.read_table(table_name)
108
+ table_reader.read
98
109
  end
99
110
 
100
111
  #
@@ -118,6 +118,14 @@ module Cequel
118
118
  TableReader.read(keyspace, name)
119
119
  end
120
120
 
121
+ #
122
+ # @param name [Symbol] name of the table to read
123
+ # @return [TableReader] object
124
+ #
125
+ def get_table_reader(name)
126
+ TableReader.get(keyspace, name)
127
+ end
128
+
121
129
  #
122
130
  # Create a table in the keyspace
123
131
  #
@@ -190,6 +198,16 @@ module Cequel
190
198
  keyspace.execute("DROP TABLE #{name}")
191
199
  end
192
200
 
201
+ #
202
+ # Drop this materialized view from the keyspace
203
+ #
204
+ # @param name [Symbol] name of the materialized view to drop
205
+ # @return [void]
206
+ #
207
+ def drop_materialized_view(name)
208
+ keyspace.execute("DROP MATERIALIZED VIEW #{name}")
209
+ end
210
+
193
211
  #
194
212
  # Create or update a table to match a given schema structure. The desired
195
213
  # schema structure is defined by the directives given in the block; this
@@ -29,6 +29,16 @@ module Cequel
29
29
  new(keyspace, table_name).read
30
30
  end
31
31
 
32
+ #
33
+ # Return a {TableReader} instance
34
+ #
35
+ # @param (see #initialize)
36
+ # @return [TableReader] object
37
+ #
38
+ def self.get(keyspace, table_name)
39
+ new(keyspace, table_name)
40
+ end
41
+
32
42
  #
33
43
  # @param keyspace [Metal::Keyspace] keyspace to read the table from
34
44
  # @param table_name [Symbol] name of the table to read
@@ -59,6 +69,18 @@ module Cequel
59
69
  end
60
70
  end
61
71
 
72
+ #
73
+ # Check if it is materialized view
74
+ #
75
+ # @return [boolean] true if it is materialized view
76
+ #
77
+ # @api private
78
+ #
79
+ def materialized_view?
80
+ cluster.keyspace(keyspace.name)
81
+ .has_materialized_view?(table_name.to_s)
82
+ end
83
+
62
84
  protected
63
85
 
64
86
  attr_reader :keyspace, :table_name, :table
@@ -150,18 +172,20 @@ module Cequel
150
172
  end
151
173
 
152
174
  def table_data
153
- @table_data ||=
154
- begin
155
- cluster = keyspace.cluster
156
- cluster.refresh_schema
175
+ @table_data ||= cluster.keyspace(keyspace.name)
176
+ .table(table_name.to_s)
177
+ end
157
178
 
158
- fail(NoSuchKeyspaceError, "No such keyspace #{keyspace.name}") if
159
- !cluster.has_keyspace?(keyspace.name)
179
+ def cluster
180
+ @cluster ||= begin
181
+ cluster = keyspace.cluster
182
+ cluster.refresh_schema
160
183
 
161
- cluster
162
- .keyspace(keyspace.name)
163
- .table(table_name.to_s)
164
- end
184
+ fail(NoSuchKeyspaceError, "No such keyspace #{keyspace.name}") if
185
+ !cluster.has_keyspace?(keyspace.name)
186
+
187
+ cluster
188
+ end
165
189
  end
166
190
  end
167
191
  end
data/lib/cequel/type.rb CHANGED
@@ -195,7 +195,7 @@ module Cequel
195
195
  #
196
196
  # `ascii` columns store 7-bit ASCII character data
197
197
  #
198
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
198
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
199
199
  # CQL3 data type documentation
200
200
  #
201
201
  class Ascii < String
@@ -215,7 +215,7 @@ module Cequel
215
215
  # `blob` columns store arbitrary bytes of data, represented as 8-bit ASCII
216
216
  # strings of hex digits
217
217
  #
218
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
218
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
219
219
  # CQL3 data type documentation
220
220
  #
221
221
  class Blob < String
@@ -239,7 +239,7 @@ module Cequel
239
239
  #
240
240
  # `boolean` types store boolean values
241
241
  #
242
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
242
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
243
243
  # CQL3 data type documentation
244
244
  #
245
245
  class Boolean < Base
@@ -256,7 +256,7 @@ module Cequel
256
256
  # counter columns cannot be updated without Cassandra internally reading
257
257
  # the existing state of the column
258
258
  #
259
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
259
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
260
260
  # CQL3 data type documentation
261
261
  #
262
262
  class Counter < Base
@@ -277,7 +277,7 @@ module Cequel
277
277
  #
278
278
  # `decimal` columns store decimal numeric values
279
279
  #
280
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
280
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
281
281
  # CQL3 data type documentation
282
282
  #
283
283
  class Decimal < Base
@@ -290,7 +290,7 @@ module Cequel
290
290
  #
291
291
  # `double` columns store 64-bit floating-point numeric values
292
292
  #
293
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
293
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
294
294
  # CQL3 data type documentation
295
295
  #
296
296
  class Double < Base
@@ -303,7 +303,7 @@ module Cequel
303
303
  #
304
304
  # `inet` columns store IP addresses
305
305
  #
306
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
306
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
307
307
  # CQL3 data type documentation
308
308
  #
309
309
  class Inet < Base
@@ -316,7 +316,7 @@ module Cequel
316
316
  #
317
317
  # `int` columns store 32-bit integer values
318
318
  #
319
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
319
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
320
320
  # CQL3 data type documentation
321
321
  #
322
322
  class Int < Base
@@ -333,7 +333,7 @@ module Cequel
333
333
  #
334
334
  # `float` columns store 32-bit floating-point numeric values
335
335
  #
336
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
336
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
337
337
  # CQL3 data type documentation
338
338
  #
339
339
  class Float < Double; end
@@ -342,7 +342,7 @@ module Cequel
342
342
  #
343
343
  # `bigint` columns store 64-bit integer values
344
344
  #
345
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
345
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
346
346
  # CQL3 data type documentation
347
347
  #
348
348
  class Bigint < Int
@@ -357,7 +357,7 @@ module Cequel
357
357
  # `varchar` columns; the names can be used interchangeably. Text columns do
358
358
  # not have a length limit
359
359
  #
360
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
360
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
361
361
  # CQL3 data type documentation
362
362
  #
363
363
  class Text < String
@@ -406,7 +406,7 @@ module Cequel
406
406
  # created using the {Cequel.uuid} method, and a value can be checked to see
407
407
  # if it is a UUID recognized by Cequel using the {Cequel.uuid?} method.
408
408
  #
409
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
409
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
410
410
  # CQL3 data type documentation
411
411
  #
412
412
  class Uuid < Base
@@ -435,7 +435,7 @@ module Cequel
435
435
  # functionality presumes the use of type 1 UUIDs, which encode the
436
436
  # timestamp of their creation.
437
437
  #
438
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
438
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
439
439
  # CQL3 data type documentation
440
440
  #
441
441
  class Timeuuid < Uuid
@@ -452,7 +452,7 @@ module Cequel
452
452
  #
453
453
  # `varint` columns store arbitrary-length integer data
454
454
  #
455
- # @see http://cassandra.apache.org/doc/cql3/CQL.html#types
455
+ # @see https://cassandra.apache.org/doc/latest/cql/types.html
456
456
  # CQL3 data type documentation
457
457
  #
458
458
  class Varint < Int
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module Cequel
3
3
  # The current version of the library
4
- VERSION = '2.0.2'
4
+ VERSION = '2.0.3'
5
5
  end
@@ -115,6 +115,100 @@ describe Cequel::Metal::Keyspace do
115
115
  expect(connect.client_compression).to eq client_compression
116
116
  end
117
117
  end
118
+
119
+ describe '#cassandra_options' do
120
+ let(:cassandra_options) { {foo: :bar} }
121
+ let(:connect) do
122
+ Cequel.connect host: Cequel::SpecSupport::Helpers.host,
123
+ port: Cequel::SpecSupport::Helpers.port,
124
+ cassandra_options: cassandra_options
125
+ end
126
+ it 'passes the cassandra options as part of the client options' do
127
+ expect(connect.send(:client_options)).to have_key(:foo)
128
+ end
129
+ end
130
+
131
+ describe 'cassandra error handling' do
132
+ let(:connect_options) do
133
+ {
134
+ host: Cequel::SpecSupport::Helpers.host,
135
+ port: Cequel::SpecSupport::Helpers.port
136
+ }
137
+ end
138
+
139
+ let(:default_connect) do
140
+ Cequel.connect(connect_options)
141
+ end
142
+
143
+ class SpecCassandraErrorHandler
144
+ def initialize(options = {})
145
+ end
146
+
147
+ def execute_stmt(keyspace)
148
+ yield
149
+ end
150
+ end
151
+
152
+ it 'uses the error handler passed in as a string' do
153
+ obj = Cequel.connect connect_options.merge(
154
+ cassandra_error_policy: 'SpecCassandraErrorHandler')
155
+
156
+ expect(obj.error_policy.class).to equal(SpecCassandraErrorHandler)
157
+ end
158
+
159
+ it 'uses the error handler passed in as a module' do
160
+ obj = Cequel.connect connect_options.merge(
161
+ cassandra_error_policy: SpecCassandraErrorHandler)
162
+
163
+ expect(obj.error_policy.class).to equal(SpecCassandraErrorHandler)
164
+ end
165
+
166
+ it 'uses the instance of an error handler passed in' do
167
+ policy = SpecCassandraErrorHandler.new
168
+
169
+ obj = Cequel.connect connect_options.merge(
170
+ cassandra_error_policy: policy)
171
+
172
+ expect(obj.error_policy).to equal(policy)
173
+ end
174
+
175
+ it 'responds to error policy' do
176
+ # Always defined, even if config does not specify it
177
+ expect(default_connect).to respond_to(:error_policy)
178
+ end
179
+
180
+ it 'calls execute_stmt on the error policy' do
181
+ policy = ::Cequel::Metal::Policy::CassandraError::RetryPolicy.new
182
+
183
+ obj = Cequel.connect connect_options.merge(
184
+ cassandra_error_policy: policy)
185
+ expect(policy).to receive(:execute_stmt).at_least(:once)
186
+ obj.execute_with_options(Cequel::Metal::Statement.new('select * from system.peers;'))
187
+ end
188
+
189
+ it 'rejects a negative value for retry delay' do
190
+ expect { Cequel.connect connect_options.merge(
191
+ retry_delay: -1.0)
192
+ }.to raise_error(ArgumentError)
193
+ end
194
+
195
+ it 'accepts a configured value for retry delay' do
196
+ obj = Cequel.connect connect_options.merge(
197
+ retry_delay: 1337.0)
198
+
199
+ # do not compare floats exactly, it is error prone
200
+ # the value is passed to the error policy
201
+ expect(obj.error_policy.retry_delay).to be_within(0.1).of(1337.0)
202
+ end
203
+
204
+ it 'can clear active connections' do
205
+ expect {
206
+ default_connect.clear_active_connections!
207
+ }.to change {
208
+ default_connect.client
209
+ }
210
+ end
211
+ end
118
212
 
119
213
  describe "#execute" do
120
214
  let(:statement) { "SELECT id FROM posts" }
@@ -396,4 +396,40 @@ describe Cequel::Schema::TableReader do
396
396
  [Cequel::Schema::DataColumn.new(:value, :text)]
397
397
  ) }
398
398
  end
399
+
400
+ describe 'materialized view exists', thrift: true do
401
+ let!(:name) { table_name }
402
+ let(:view_name) { "#{name}_view" }
403
+ before do
404
+ cequel.execute <<-CQL
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
434
+ end
399
435
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat Brown
@@ -29,7 +29,7 @@ authors:
29
29
  autorequire:
30
30
  bindir: bin
31
31
  cert_chain: []
32
- date: 2016-10-17 00:00:00.000000000 Z
32
+ date: 2017-01-06 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: activemodel
@@ -219,6 +219,7 @@ files:
219
219
  - lib/cequel/metal/keyspace.rb
220
220
  - lib/cequel/metal/logging.rb
221
221
  - lib/cequel/metal/new_relic_instrumentation.rb
222
+ - lib/cequel/metal/policy/cassandra_error.rb
222
223
  - lib/cequel/metal/request_logger.rb
223
224
  - lib/cequel/metal/row.rb
224
225
  - lib/cequel/metal/row_specification.rb