cassandra-driver 2.1.7-java → 3.0.0.beta.1-java

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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -53
  3. data/lib/cassandra.rb +22 -3
  4. data/lib/cassandra/aggregate.rb +109 -0
  5. data/lib/cassandra/argument.rb +51 -0
  6. data/lib/cassandra/auth/providers/password.rb +7 -4
  7. data/lib/cassandra/cluster.rb +14 -3
  8. data/lib/cassandra/cluster/client.rb +56 -34
  9. data/lib/cassandra/cluster/connector.rb +6 -6
  10. data/lib/cassandra/cluster/control_connection.rb +204 -251
  11. data/lib/cassandra/cluster/metadata.rb +2 -0
  12. data/lib/cassandra/cluster/schema.rb +131 -209
  13. data/lib/cassandra/cluster/schema/cql_type_parser.rb +104 -0
  14. data/lib/cassandra/cluster/schema/fetchers.rb +1174 -0
  15. data/lib/cassandra/cluster/schema/{type_parser.rb → fqcn_type_parser.rb} +7 -3
  16. data/lib/cassandra/column.rb +2 -2
  17. data/lib/cassandra/driver.rb +27 -9
  18. data/lib/cassandra/errors.rb +179 -25
  19. data/lib/cassandra/execution/info.rb +8 -1
  20. data/lib/cassandra/execution/options.rb +34 -0
  21. data/lib/cassandra/execution/trace.rb +42 -10
  22. data/lib/cassandra/function.rb +150 -0
  23. data/lib/cassandra/future.rb +66 -35
  24. data/lib/cassandra/host.rb +7 -4
  25. data/lib/cassandra/keyspace.rb +112 -13
  26. data/lib/cassandra/load_balancing.rb +1 -1
  27. data/lib/cassandra/protocol.rb +9 -3
  28. data/lib/cassandra/protocol/coder.rb +434 -155
  29. data/lib/cassandra/protocol/cql_byte_buffer.rb +43 -0
  30. data/lib/cassandra/protocol/cql_protocol_handler.rb +4 -1
  31. data/lib/cassandra/protocol/request.rb +4 -0
  32. data/lib/cassandra/protocol/requests/auth_response_request.rb +5 -1
  33. data/lib/cassandra/protocol/requests/batch_request.rb +7 -2
  34. data/lib/cassandra/protocol/requests/credentials_request.rb +5 -1
  35. data/lib/cassandra/protocol/requests/execute_request.rb +16 -10
  36. data/lib/cassandra/protocol/requests/prepare_request.rb +12 -3
  37. data/lib/cassandra/protocol/requests/query_request.rb +20 -11
  38. data/lib/cassandra/protocol/responses/already_exists_error_response.rb +4 -4
  39. data/lib/cassandra/protocol/responses/error_response.rb +14 -14
  40. data/lib/cassandra/protocol/responses/function_failure_error_response.rb +41 -0
  41. data/lib/cassandra/protocol/responses/prepared_result_response.rb +12 -9
  42. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +5 -3
  43. data/lib/cassandra/protocol/responses/read_failure_error_response.rb +43 -0
  44. data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +4 -4
  45. data/lib/cassandra/protocol/responses/ready_response.rb +5 -1
  46. data/lib/cassandra/protocol/responses/result_response.rb +3 -3
  47. data/lib/cassandra/protocol/responses/rows_result_response.rb +2 -2
  48. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +25 -24
  49. data/lib/cassandra/protocol/responses/schema_change_result_response.rb +20 -23
  50. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +2 -2
  51. data/lib/cassandra/protocol/responses/unavailable_error_response.rb +4 -4
  52. data/lib/cassandra/protocol/responses/unprepared_error_response.rb +4 -4
  53. data/lib/cassandra/protocol/responses/write_failure_error_response.rb +45 -0
  54. data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +4 -4
  55. data/lib/cassandra/protocol/v1.rb +38 -13
  56. data/lib/cassandra/protocol/v3.rb +34 -29
  57. data/lib/cassandra/protocol/v4.rb +334 -0
  58. data/lib/cassandra/result.rb +10 -9
  59. data/lib/cassandra/retry.rb +17 -3
  60. data/lib/cassandra/retry/policies/default.rb +9 -3
  61. data/lib/cassandra/session.rb +15 -7
  62. data/lib/cassandra/statement.rb +5 -0
  63. data/lib/cassandra/statements/batch.rb +36 -12
  64. data/lib/cassandra/statements/bound.rb +2 -1
  65. data/lib/cassandra/statements/prepared.rb +106 -35
  66. data/lib/cassandra/statements/simple.rb +4 -2
  67. data/lib/cassandra/table.rb +70 -105
  68. data/lib/cassandra/time.rb +98 -0
  69. data/lib/cassandra/time_uuid.rb +1 -1
  70. data/lib/cassandra/tuple.rb +7 -0
  71. data/lib/cassandra/types.rb +472 -272
  72. data/lib/cassandra/udt.rb +10 -0
  73. data/lib/cassandra/util.rb +32 -1
  74. data/lib/cassandra/uuid.rb +6 -1
  75. data/lib/cassandra/uuid/generator.rb +7 -7
  76. data/lib/cassandra/version.rb +1 -1
  77. data/lib/cassandra_murmur3.jar +0 -0
  78. data/lib/datastax/cassandra.rb +5 -2
  79. metadata +27 -17
@@ -49,6 +49,19 @@ module Cassandra
49
49
  # @see Cassandra::Result#paging_state
50
50
  attr_reader :paging_state
51
51
 
52
+ # @return [nil, Hash<String, String>] custom outgoing payload, a map of
53
+ # string and byte buffers.
54
+ #
55
+ # @see https://github.com/apache/cassandra/blob/33f1edcce97779c971d4f78712a9a8bf014ffbbc/doc/native_protocol_v4.spec#L127-L133 Description of custom payload in Cassandra native protocol v4.
56
+ # @see https://datastax.github.io/java-driver/features/custom_payloads/#enabling-custom-payloads-on-c-nodes Enabling custom payloads on Cassandra nodes.
57
+ #
58
+ # @example Sending a custom payload
59
+ # result = session.execute(payload: {
60
+ # 'some key' => Cassandra::Protocol::CqlByteBuffer.new
61
+ # .append_string('some value')
62
+ # })
63
+ attr_reader :payload
64
+
52
65
  # @private
53
66
  def initialize(options)
54
67
  consistency = options[:consistency]
@@ -59,6 +72,8 @@ module Cassandra
59
72
  paging_state = options[:paging_state]
60
73
  arguments = options[:arguments]
61
74
  type_hints = options[:type_hints]
75
+ idempotent = options[:idempotent]
76
+ payload = options[:payload]
62
77
 
63
78
  Util.assert_one_of(CONSISTENCIES, consistency) { ":consistency must be one of #{CONSISTENCIES.inspect}, #{consistency.inspect} given" }
64
79
 
@@ -94,6 +109,17 @@ module Cassandra
94
109
  Util.assert_instance_of_one_of([::Array, ::Hash], type_hints) { ":type_hints must be an Array or a Hash, #{type_hints.inspect} given" }
95
110
  end
96
111
 
112
+ unless payload.nil?
113
+ Util.assert_instance_of(::Hash, payload) { ":payload must be a Hash" }
114
+ Util.assert_not_empty(payload) { ":payload must not be empty" }
115
+ Util.assert(payload.size <= 65535) { ":payload cannot contain more than 65535 key/value pairs" }
116
+
117
+ payload = payload.each_with_object(::Hash.new) do |(key, value), payload|
118
+ payload[String(key)] = String(value)
119
+ end
120
+ payload.freeze
121
+ end
122
+
97
123
  @consistency = consistency
98
124
  @page_size = page_size
99
125
  @trace = !!trace
@@ -102,6 +128,8 @@ module Cassandra
102
128
  @paging_state = paging_state
103
129
  @arguments = arguments
104
130
  @type_hints = type_hints
131
+ @idempotent = !!idempotent
132
+ @payload = payload
105
133
  end
106
134
 
107
135
  # @return [Boolean] whether request tracing was enabled
@@ -109,6 +137,12 @@ module Cassandra
109
137
  @trace
110
138
  end
111
139
 
140
+ # @return [Boolean] whether statement can be retried on timeout
141
+ def idempotent?
142
+ @idempotent
143
+ end
144
+
145
+ # @private
112
146
  def eql?(other)
113
147
  other.is_a?(Options) &&
114
148
  other.consistency == @consistency &&
@@ -39,6 +39,7 @@ module Cassandra
39
39
  @thread = thread
40
40
  end
41
41
 
42
+ # @private
42
43
  def ==(other)
43
44
  other == @id
44
45
  end
@@ -53,41 +54,61 @@ module Cassandra
53
54
 
54
55
  # @private
55
56
  def initialize(id, client)
56
- @id = id
57
- @client = client
57
+ @id = id
58
+ @client = client
59
+ @coordinator = nil
60
+ @duration = nil
61
+ @parameters = nil
62
+ @request = nil
63
+ @started_at = nil
64
+ @events = nil
65
+ @client_ip = nil
66
+ @loaded = false
67
+ @loaded_events = false
58
68
 
59
69
  mon_initialize
60
70
  end
61
71
 
62
- # Returns the ip of coordinator node. Typically the same as {Cassandra::Execution::Info#hosts}`.last`
72
+ # Returns the ip of coordinator node. Typically the same as
73
+ # {Cassandra::Execution::Info#hosts}`.last`
63
74
  #
64
75
  # @return [IPAddr] ip of the coordinator node
65
76
  def coordinator
66
- load unless @coordinator
77
+ load unless @loaded
67
78
 
68
79
  @coordinator
69
80
  end
70
81
 
82
+ # Returns the ip of the client node, the node that ran the driver
83
+ # instance that started tracing.
84
+ #
85
+ # @return [IPAddr, nil] ip of the client node running the driver
86
+ def client
87
+ load unless @loaded
88
+
89
+ @client_ip
90
+ end
91
+
71
92
  def duration
72
- load unless @duration
93
+ load unless @loaded
73
94
 
74
95
  @duration
75
96
  end
76
97
 
77
98
  def parameters
78
- load unless @parameters
99
+ load unless @loaded
79
100
 
80
101
  @parameters
81
102
  end
82
103
 
83
104
  def request
84
- load unless @request
105
+ load unless @loaded
85
106
 
86
107
  @request
87
108
  end
88
109
 
89
110
  def started_at
90
- load unless @started_at
111
+ load unless @loaded
91
112
 
92
113
  @started_at
93
114
  end
@@ -96,11 +117,12 @@ module Cassandra
96
117
  #
97
118
  # @return [Array<Cassandra::Execution::Trace::Event>] events
98
119
  def events
99
- load_events unless @events
120
+ load_events unless @loaded_events
100
121
 
101
122
  @events
102
123
  end
103
124
 
125
+ # @private
104
126
  def inspect
105
127
  "#<#{self.class.name}:0x#{self.object_id.to_s(16)} @id=#{@id.inspect}>"
106
128
  end
@@ -117,7 +139,16 @@ module Cassandra
117
139
  synchronize do
118
140
  return if @loaded
119
141
 
120
- data = @client.query(Statements::Simple.new(SELECT_SESSION % @id), VOID_OPTIONS).get.first
142
+ attempt = 1
143
+ data = @client.query(Statements::Simple.new(SELECT_SESSION % @id), VOID_OPTIONS).get.first
144
+
145
+ while data.nil? && attempt <= 5
146
+ sleep(attempt * 0.4)
147
+ data = @client.query(Statements::Simple.new(SELECT_SESSION % @id), VOID_OPTIONS).get.first
148
+ break if data
149
+ attempt += 1
150
+ end
151
+
121
152
  raise ::RuntimeError, "unable to load trace #{@id}" if data.nil?
122
153
 
123
154
  @coordinator = data['coordinator']
@@ -125,6 +156,7 @@ module Cassandra
125
156
  @parameters = data['parameters']
126
157
  @request = data['request']
127
158
  @started_at = data['started_at']
159
+ @client_ip = data['client']
128
160
  @loaded = true
129
161
  end
130
162
 
@@ -0,0 +1,150 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2015 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ # Represents a cassandra user defined function
21
+ # @see Cassandra::Keyspace#each_function
22
+ # @see Cassandra::Keyspace#function
23
+ # @see Cassandra::Keyspace#has_function?
24
+ class Function
25
+ # @private
26
+ attr_reader :keyspace
27
+ # @return [String] function name
28
+ attr_reader :name
29
+ # @return [String] function language
30
+ attr_reader :language
31
+ # @return [Cassandra::Type] function return type
32
+ attr_reader :type
33
+ # @return [String] function body
34
+ attr_reader :body
35
+
36
+ # @private
37
+ def initialize(keyspace, name, language, type, arguments, body, called_on_null)
38
+ @keyspace = keyspace
39
+ @name = name
40
+ @language = language
41
+ @type = type
42
+ @arguments = arguments
43
+ @body = body
44
+ @called_on_null = called_on_null
45
+
46
+ # Build up an arguments hash keyed on arg-name.
47
+ @arguments_hash = @arguments.each_with_object({}) do |arg, h|
48
+ h[arg.name] = arg
49
+ end
50
+ end
51
+
52
+ # @return [Boolean] whether this function will be called on null input
53
+ def called_on_null?
54
+ @called_on_null
55
+ end
56
+
57
+ # @param name [String] argument name
58
+ # @return [Boolean] whether this function has a given argument
59
+ def has_argument?(name)
60
+ @arguments_hash.has_key?(name)
61
+ end
62
+
63
+ # @param name [String] argument name
64
+ # @return [Cassandra::Argument, nil] an argument or nil
65
+ def argument(name)
66
+ @arguments_hash[name]
67
+ end
68
+
69
+ # Yield or enumerate each argument defined in this function
70
+ # @overload each_argument
71
+ # @yieldparam argument [Cassandra::Argument] current argument
72
+ # @return [Cassandra::Table] self
73
+ # @overload each_argument
74
+ # @return [Array<Cassandra::Argument>] a list of arguments
75
+ def each_argument(&block)
76
+ if block_given?
77
+ @arguments.each(&block)
78
+ self
79
+ else
80
+ # We return a dup of the arguments so that the caller can manipulate
81
+ # the array however they want without affecting the source.
82
+ @arguments.dup
83
+ end
84
+ end
85
+ alias :arguments :each_argument
86
+
87
+ # Get the list of argument types for this function.
88
+ # @return [Array<Cassandra::Type>] a list of argument types.
89
+ def argument_types
90
+ @arguments.map { |argument| argument.type }
91
+ end
92
+
93
+ # @private
94
+ def eql?(other)
95
+ other.is_a?(Function) && \
96
+ @keyspace == other.keyspace && \
97
+ @name == other.name && \
98
+ @language == other.language && \
99
+ @type == other.type && \
100
+ @arguments == other.arguments && \
101
+ @body == other.body && \
102
+ @called_on_null == other.called_on_null?
103
+ end
104
+ alias :== :eql?
105
+
106
+ # @private
107
+ def hash
108
+ @hash ||= begin
109
+ h = 17
110
+ h = 31 * h + @keyspace.hash
111
+ h = 31 * h + @name.hash
112
+ h = 31 * h + @language.hash
113
+ h = 31 * h + @type.hash
114
+ h = 31 * h + @arguments.hash
115
+ h = 31 * h + @body.hash
116
+ h = 31 * h + @called_on_null.hash
117
+ h
118
+ end
119
+ end
120
+
121
+ # @private
122
+ def inspect
123
+ "#<Cassandra::Function:0x#{self.object_id.to_s(16)} @keyspace=#{@keyspace.inspect}, @name=#{@name.inspect}, @language=#{@language.inspect}, @type=#{@type.inspect}, @arguments=#{@arguments.inspect} @body=#{@body.inspect}>"
124
+ end
125
+
126
+ # @return [String] a cql representation of this function
127
+ def to_cql
128
+ cql = "CREATE FUNCTION #{Util.escape_name(@keyspace)}.#{Util.escape_name(@name)}("
129
+ first = true
130
+ @arguments.each do |argument|
131
+ if first
132
+ first = false
133
+ else
134
+ cql << ', '
135
+ end
136
+ cql << "#{argument.name} #{argument.type}"
137
+ end
138
+ cql << ")"
139
+ if @called_on_null
140
+ cql << "\n CALLED ON NULL INPUT"
141
+ else
142
+ cql << "\n RETURNS NULL ON NULL INPUT"
143
+ end
144
+ cql << "\n RETURNS #{@type}"
145
+ cql << "\n LANGUAGE #{@language}"
146
+ cql << "\n AS $$#{@body}$$"
147
+ cql << ";"
148
+ end
149
+ end
150
+ end
@@ -51,10 +51,12 @@ module Cassandra
51
51
  @error = error
52
52
  end
53
53
 
54
- def get
54
+ def get(timeout = nil)
55
55
  raise(@error, @error.message, @error.backtrace)
56
56
  end
57
57
 
58
+ alias :join :get
59
+
58
60
  def on_success
59
61
  raise ::ArgumentError, "no block given" unless block_given?
60
62
  self
@@ -81,10 +83,6 @@ module Cassandra
81
83
  self
82
84
  end
83
85
 
84
- def join
85
- self
86
- end
87
-
88
86
  def then
89
87
  raise ::ArgumentError, "no block given" unless block_given?
90
88
  self
@@ -109,10 +107,12 @@ module Cassandra
109
107
  @value = value
110
108
  end
111
109
 
112
- def get
110
+ def get(timeout = nil)
113
111
  @value
114
112
  end
115
113
 
114
+ alias :join :get
115
+
116
116
  def on_success
117
117
  raise ::ArgumentError, "no block given" unless block_given?
118
118
  yield(@value) rescue nil
@@ -356,23 +356,23 @@ module Cassandra
356
356
  end
357
357
 
358
358
  # Returns future value or raises future error
359
- # @note This method blocks until a future is resolved
360
- # @raise [Exception] error used to resolve this future if any
361
- # @return [Object] value used to resolve this future if any
362
- def get
363
- @signal.get
364
-
359
+ #
360
+ # @note This method blocks until a future is resolved or a times out
361
+ #
362
+ # @param timeout [nil, Numeric] a maximum number of seconds to block
363
+ # current thread for while waiting for this future to resolve. Will
364
+ # wait indefinitely if passed `nil`.
365
+ #
366
+ # @raise [Errors::TimeoutError] raised when wait time exceeds the timeout
367
+ # @raise [Exception] raises when the future has been resolved with an
368
+ # error. The original exception will be raised.
369
+ #
370
+ # @return [Object] the value that the future has been resolved with
371
+ def get(timeout = nil)
372
+ @signal.get(timeout)
365
373
  end
366
374
 
367
- # Block until the future has been resolved
368
- # @note This method blocks until a future is resolved
369
- # @note This method won't raise any errors or return anything but the
370
- # future itself
371
- # @return [self]
372
- def join
373
- @signal.join
374
- self
375
- end
375
+ alias :join :get
376
376
  end
377
377
 
378
378
  # @private
@@ -550,28 +550,59 @@ module Cassandra
550
550
  self
551
551
  end
552
552
 
553
- def join
554
- return unless @state == :pending
555
-
556
- synchronize do
557
- return unless @state == :pending
558
-
559
- @waiting += 1
560
- @cond.wait while @state == :pending
561
- @waiting -= 1
553
+ # @param timeout [nil, Numeric] a maximum number of seconds to block
554
+ # current thread for while waiting for this future to resolve. Will
555
+ # wait indefinitely if passed `nil`.
556
+ #
557
+ # @raise [ArgumentError] raised when a negative timeout is given
558
+ # @raise [Errors::TimeoutError] raised when wait time exceeds the timeout
559
+ # @raise [Exception] raises when the future has been resolved with an
560
+ # error. The original exception will be raised.
561
+ #
562
+ # @return [Object] the value that the future has been resolved with
563
+ def get(timeout = nil)
564
+ timeout = timeout && Float(timeout)
565
+
566
+ if timeout
567
+ raise ::ArgumentError, "timeout cannot be negative, #{timeout.inspect} given" if timeout < 0
568
+
569
+ start = ::Time.now
570
+ now = start
571
+ deadline = start + timeout
562
572
  end
563
573
 
564
- nil
565
- end
574
+ if @state == :pending
575
+ synchronize do
576
+ if @state == :pending
577
+ @waiting += 1
578
+ while @state == :pending
579
+ if deadline
580
+ @cond.wait(deadline - now)
581
+ now = ::Time.now
582
+ break if now >= deadline
583
+ else
584
+ @cond.wait
585
+ end
586
+ end
587
+ @waiting -= 1
588
+ end
589
+ end
566
590
 
567
- def get
568
- join if @state == :pending
591
+ if @state == :pending
592
+ total_wait = deadline - start
593
+ raise Errors::TimeoutError, "Future did not complete within #{timeout.inspect} seconds. Wait time: #{total_wait.inspect}"
594
+ end
595
+ end
569
596
 
570
- raise(@error, @error.message, @error.backtrace) if @state == :broken
597
+ if @state == :broken
598
+ raise(@error, @error.message, @error.backtrace)
599
+ end
571
600
 
572
601
  @value
573
602
  end
574
603
 
604
+ alias :join :get
605
+
575
606
  def add_listener(listener)
576
607
  if @state == :pending
577
608
  synchronize do