vertica 0.8.1 → 0.9.0.beta1

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 (50) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +23 -0
  3. data/Rakefile +12 -9
  4. data/VERSION +1 -1
  5. data/lib/vertica.rb +2 -7
  6. data/lib/vertica/connection.rb +47 -57
  7. data/lib/vertica/messages/backend_messages/authentication.rb +6 -7
  8. data/lib/vertica/messages/backend_messages/backend_key_data.rb +4 -7
  9. data/lib/vertica/messages/backend_messages/bind_complete.rb +1 -1
  10. data/lib/vertica/messages/backend_messages/close_complete.rb +1 -1
  11. data/lib/vertica/messages/backend_messages/command_complete.rb +13 -7
  12. data/lib/vertica/messages/backend_messages/data_row.rb +10 -10
  13. data/lib/vertica/messages/backend_messages/empty_query_response.rb +1 -1
  14. data/lib/vertica/messages/backend_messages/error_response.rb +2 -30
  15. data/lib/vertica/messages/backend_messages/no_data.rb +1 -1
  16. data/lib/vertica/messages/backend_messages/notice_response.rb +33 -7
  17. data/lib/vertica/messages/backend_messages/notification_response.rb +4 -9
  18. data/lib/vertica/messages/backend_messages/parameter_description.rb +5 -9
  19. data/lib/vertica/messages/backend_messages/parameter_status.rb +4 -8
  20. data/lib/vertica/messages/backend_messages/parse_complete.rb +1 -1
  21. data/lib/vertica/messages/backend_messages/portal_suspended.rb +1 -1
  22. data/lib/vertica/messages/backend_messages/ready_for_query.rb +3 -4
  23. data/lib/vertica/messages/backend_messages/row_description.rb +15 -15
  24. data/lib/vertica/messages/backend_messages/unknown.rb +2 -2
  25. data/lib/vertica/messages/frontend_messages/bind.rb +3 -12
  26. data/lib/vertica/messages/frontend_messages/cancel_request.rb +1 -5
  27. data/lib/vertica/messages/frontend_messages/close.rb +5 -9
  28. data/lib/vertica/messages/frontend_messages/describe.rb +5 -9
  29. data/lib/vertica/messages/frontend_messages/execute.rb +2 -6
  30. data/lib/vertica/messages/frontend_messages/flush.rb +1 -1
  31. data/lib/vertica/messages/frontend_messages/parse.rb +2 -8
  32. data/lib/vertica/messages/frontend_messages/password.rb +5 -6
  33. data/lib/vertica/messages/frontend_messages/query.rb +2 -3
  34. data/lib/vertica/messages/frontend_messages/ssl_request.rb +1 -2
  35. data/lib/vertica/messages/frontend_messages/startup.rb +6 -8
  36. data/lib/vertica/messages/frontend_messages/sync.rb +1 -1
  37. data/lib/vertica/messages/frontend_messages/terminate.rb +1 -1
  38. data/lib/vertica/messages/message.rb +9 -18
  39. data/lib/vertica/result.rb +8 -1
  40. data/test/functional/connection_test.rb +73 -0
  41. data/test/functional/query_test.rb +150 -0
  42. data/test/test_helper.rb +7 -13
  43. data/test/unit/backend_message_test.rb +106 -0
  44. metadata +72 -20
  45. data/lib/vertica/bit_helper.rb +0 -34
  46. data/lib/vertica/core_ext/numeric.rb +0 -13
  47. data/lib/vertica/core_ext/string.rb +0 -22
  48. data/lib/vertica/notice.rb +0 -11
  49. data/lib/vertica/vertica_socket.rb +0 -19
  50. data/test/connection_test.rb +0 -191
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,23 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ vertica (0.8.1)
5
+ vertica
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ git (1.2.5)
11
+ jeweler (1.6.4)
12
+ bundler (~> 1.0)
13
+ git (>= 1.2.5)
14
+ rake
15
+ rake (0.9.2)
16
+
17
+ PLATFORMS
18
+ java
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ jeweler
23
+ vertica!
data/Rakefile CHANGED
@@ -15,7 +15,10 @@ begin
15
15
  gem.files = FileList["[A-Z]*", 'lib/**/*.rb'].to_a
16
16
 
17
17
  gem.test_files = FileList['test/**/*.rb']
18
-
18
+
19
+ gem.add_development_dependency 'jeweler'
20
+ gem.add_development_dependency 'rake'
21
+
19
22
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
23
  end
21
24
 
@@ -23,14 +26,14 @@ begin
23
26
  rescue LoadError
24
27
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
25
28
  end
26
- #
27
- # require 'rake/testtask'
28
- # Rake::TestTask.new(:test) do |test|
29
- # test.libs << 'lib' << 'spec'
30
- # test.pattern = 'spec/**/*_spec.rb'
31
- # test.verbose = true
32
- # end
33
- #
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/*_test.rb'
34
+ test.verbose = true
35
+ end
36
+
34
37
  # begin
35
38
  # require 'rcov/rcovtask'
36
39
  # Rcov::RcovTask.new do |test|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.1
1
+ 0.9.0.beta1
data/lib/vertica.rb CHANGED
@@ -2,6 +2,7 @@ module Vertica
2
2
  class Error < StandardError
3
3
  class ConnectionError < Error; end
4
4
  class MessageError < Error; end
5
+ class QueryError < Error; end
5
6
  end
6
7
 
7
8
  PROTOCOL_VERSION = 3 << 16
@@ -20,12 +21,6 @@ end
20
21
  bigdecimal/util
21
22
  date
22
23
 
23
- vertica/core_ext/numeric
24
- vertica/core_ext/string
25
-
26
- vertica/bit_helper
27
- vertica/vertica_socket
28
-
29
24
  vertica/column
30
25
  vertica/result
31
26
  vertica/connection
@@ -39,9 +34,9 @@ end
39
34
  vertica/messages/backend_messages/command_complete
40
35
  vertica/messages/backend_messages/data_row
41
36
  vertica/messages/backend_messages/empty_query_response
37
+ vertica/messages/backend_messages/notice_response
42
38
  vertica/messages/backend_messages/error_response
43
39
  vertica/messages/backend_messages/no_data
44
- vertica/messages/backend_messages/notice_response
45
40
  vertica/messages/backend_messages/notification_response
46
41
  vertica/messages/backend_messages/parameter_description
47
42
  vertica/messages/backend_messages/parameter_status
@@ -1,37 +1,42 @@
1
1
  module Vertica
2
-
3
2
  class Connection
4
3
 
5
4
  STATUSES = {
6
- ?I => :no_transaction,
7
- ?T => :in_transaction,
8
- ?E => :failed_transaction
5
+ 'I' => :no_transaction,
6
+ 'T' => :in_transaction,
7
+ 'E' => :failed_transaction
9
8
  }
10
9
 
10
+ attr_reader :options, :notices, :transaction_status, :backend_pid, :backend_key, :notifications, :parameters
11
+
11
12
  def self.cancel(existing_conn)
12
13
  conn = self.new(existing_conn.options.merge(:skip_startup => true))
13
14
  conn.write Messages::CancelRequest.new(existing_conn.backend_pid, existing_conn.backend_key)
14
15
  conn.write Messages::Flush.new
15
- conn.connection.close
16
+ conn.socket.close
16
17
  end
17
18
 
18
19
  def initialize(options = {})
19
20
  reset_values
20
21
 
21
22
  @options = options
23
+ @notices = []
22
24
 
23
25
  unless options[:skip_startup]
24
- connection.write Messages::Startup.new(@options[:user], @options[:database]).to_bytes
26
+ write Messages::Startup.new(@options[:user], @options[:database])
25
27
  process
28
+
29
+ query("SET SEARCH_PATH TO #{options[:search_path]}") if options[:search_path]
30
+ query("SET ROLE #{options[:role]}") if options[:role]
26
31
  end
27
32
  end
28
33
 
29
- def connection
30
- @connection ||= begin
31
- conn = VerticaSocket.new(@options[:host], @options[:port].to_s)
34
+ def socket
35
+ @socket ||= begin
36
+ conn = TCPSocket.new(@options[:host], @options[:port].to_s)
32
37
  if @options[:ssl]
33
38
  conn.write Messages::SslRequest.new.to_bytes
34
- if conn.read_byte == ?S
39
+ if conn.read(1) == 'S'
35
40
  conn = OpenSSL::SSL::SSLSocket.new(conn, OpenSSL::SSL::SSLContext.new)
36
41
  conn.sync = true
37
42
  conn.connect
@@ -39,12 +44,17 @@ module Vertica
39
44
  raise Error::ConnectionError.new("SSL requested but server doesn't support it.")
40
45
  end
41
46
  end
47
+
42
48
  conn
43
49
  end
44
50
  end
45
51
 
52
+ def ssl?
53
+ socket.kind_of?(OpenSSL::SSL::SSLSocket)
54
+ end
55
+
46
56
  def opened?
47
- @connection && @backend_pid && @transaction_status
57
+ @socket && @backend_pid && @transaction_status
48
58
  end
49
59
 
50
60
  def closed?
@@ -52,14 +62,15 @@ module Vertica
52
62
  end
53
63
 
54
64
  def write(message)
55
- connection.write_message message
65
+ raise ArgumentError, "invalid message: (#{message.inspect})" unless message.respond_to?(:to_bytes)
66
+ socket.write message.to_bytes
56
67
  end
57
68
 
58
69
  def close
59
70
  write Messages::Terminate.new
60
- connection.shutdown
61
- @connection = nil
62
- rescue Errno::ENOTCONN # the backend closed the connection already
71
+ socket.close
72
+ @socket = nil
73
+ rescue Errno::ENOTCONN # the backend closed the socket already
63
74
  ensure
64
75
  reset_values
65
76
  end
@@ -69,34 +80,6 @@ module Vertica
69
80
  reset_values
70
81
  end
71
82
 
72
- def options
73
- @options.dup
74
- end
75
-
76
- def transaction_status
77
- @transaction_status
78
- end
79
-
80
- def backend_pid
81
- @backend_pid
82
- end
83
-
84
- def backend_key
85
- @backend_key
86
- end
87
-
88
- def notifications
89
- @notifications
90
- end
91
-
92
- def parameters
93
- @parameters.dup
94
- end
95
-
96
- def put_copy_data; raise NotImplementedError.new; end
97
- def put_copy_end; raise NotImplementedError.new; end
98
- def get_copy_data; raise NotImplementedError.new; end
99
-
100
83
  def query(query_string, &block)
101
84
  raise ArgumentError.new("Query string cannot be blank or empty.") if query_string.nil? || query_string.empty?
102
85
  reset_result
@@ -141,10 +124,25 @@ module Vertica
141
124
 
142
125
  protected
143
126
 
127
+
128
+ def read_bytes(n)
129
+ bytes = socket.read(n)
130
+ raise Vertica::Error::ConnectionError.new("Couldn't read #{n} characters from socket.") if bytes.nil? || bytes.size != n
131
+ return bytes
132
+ end
133
+
134
+ def read_message
135
+ type = read_bytes(1)
136
+ size = read_bytes(4).unpack('N').first
137
+ raise Vertica::Error::MessageError.new("Bad message size: #{size}.") unless size >= 4
138
+ Messages::BackendMessage.factory type, read_bytes(size - 4)
139
+ end
140
+
141
+
144
142
  def process(return_result = false)
145
143
  result = return_result ? Result.new : nil
146
144
  loop do
147
- case message = connection.read_message
145
+ case message = read_message
148
146
  when Messages::Authentication
149
147
  if message.code != Messages::Authentication::OK
150
148
  write Messages::Password.new(@options[:password], message.code, {:user => @options[:user], :salt => message.salt})
@@ -159,12 +157,12 @@ module Vertica
159
157
  result.add_row(message) if result && !@process_row
160
158
 
161
159
  when Messages::ErrorResponse
162
- raise Error::MessageError.new(message.error)
160
+ error_class = result ? Vertica::Error::QueryError : Vertica::Error::ConnectionError
161
+ raise error_class.new(message.error_message)
162
+
163
163
 
164
164
  when Messages::NoticeResponse
165
- message.notices.each do |notice|
166
- @notices << Notice.new(notice[0], notice[1])
167
- end
165
+ @notices << message.values
168
166
 
169
167
  when Messages::NotificationResponse
170
168
  @notifications << Notification.new(message.pid, message.condition, message.additional_info)
@@ -193,14 +191,6 @@ module Vertica
193
191
  Messages::ParseComplete,
194
192
  Messages::PortalSuspended
195
193
  break
196
- # when Messages::CopyData
197
- # # nothing
198
- # when Messages::CopyDone
199
- # # nothing
200
- # when Messages::CopyInResponse
201
- # raise 'not done'
202
- # when Messages::CopyOutResponse
203
- # raise 'not done'
204
194
  end
205
195
  end
206
196
 
@@ -214,7 +204,7 @@ module Vertica
214
204
  @backend_pid = nil
215
205
  @backend_key = nil
216
206
  @transaction_status = nil
217
- @connection = nil
207
+ @socket = nil
218
208
  @process_row = nil
219
209
  end
220
210
 
@@ -1,7 +1,7 @@
1
1
  module Vertica
2
2
  module Messages
3
3
  class Authentication < BackendMessage
4
- message_id ?R
4
+ message_id 'R'
5
5
 
6
6
  OK = 0
7
7
  KERBEROS_V5 = 2
@@ -17,12 +17,11 @@ module Vertica
17
17
  attr_reader :salt
18
18
  attr_reader :auth_data
19
19
 
20
- def initialize(stream, size)
21
- super
22
- case @code = stream.read_network_int32
23
- when CRYPT_PASSWORD then @salt = stream.readn(2)
24
- when MD5_PASSWORD then @salt = stream.readn(4)
25
- when GSS_CONTINUE then @auth_data = stream.readn(size - 9)
20
+ def initialize(data)
21
+ @code, other = data.unpack('Na*')
22
+ case @code
23
+ when CRYPT_PASSWORD, MD5_PASSWORD then @salt = other
24
+ when GSS_CONTINUE then @auth_data = other
26
25
  end
27
26
  end
28
27
  end
@@ -1,15 +1,12 @@
1
1
  module Vertica
2
2
  module Messages
3
3
  class BackendKeyData < BackendMessage
4
- message_id ?K
4
+ message_id 'K'
5
5
 
6
- attr_reader :pid
7
- attr_reader :key
6
+ attr_reader :pid, :key
8
7
 
9
- def initialize(stream, size)
10
- super
11
- @pid = stream.read_network_int32
12
- @key = stream.read_network_int32
8
+ def initialize(data)
9
+ @pid, @key = data.unpack('NN')
13
10
  end
14
11
  end
15
12
  end
@@ -1,7 +1,7 @@
1
1
  module Vertica
2
2
  module Messages
3
3
  class BindComplete < BackendMessage
4
- message_id ?2
4
+ message_id '2'
5
5
  end
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  module Vertica
2
2
  module Messages
3
3
  class CloseComplete < BackendMessage
4
- message_id ?3
4
+ message_id '3'
5
5
  end
6
6
  end
7
7
  end
@@ -1,16 +1,22 @@
1
1
  module Vertica
2
2
  module Messages
3
3
  class CommandComplete < BackendMessage
4
- message_id ?C
4
+ message_id 'C'
5
5
 
6
- attr_reader :tag
7
- attr_reader :rows
6
+ attr_reader :tag, :rows, :oid
8
7
 
9
- def initialize(stream, size)
10
- @tag = stream.read_cstring
11
- @rows = @tag.split[-1].to_i
8
+ def initialize(data)
9
+ case data = data.unpack('Z*').first
10
+ when /^INSERT /
11
+ @tag, oid, rows = data.split(' ', 3)
12
+ @oid, @rows = oid.to_i, rows.to_i
13
+ when /^DELETE /, /^UPDATE /, /^MOVE /, /^FETCH /, /^COPY /
14
+ @tag, @rows = data.split(' ', 2)
15
+ @rows = rows.to_i
16
+ else
17
+ @tag = data
18
+ end
12
19
  end
13
-
14
20
  end
15
21
  end
16
22
  end
@@ -1,18 +1,18 @@
1
1
  module Vertica
2
2
  module Messages
3
3
  class DataRow < BackendMessage
4
- message_id ?D
4
+ message_id 'D'
5
5
 
6
- attr_reader :field_count
7
- attr_reader :fields
6
+ attr_reader :values
8
7
 
9
- def initialize(stream, size)
10
- @fields = []
11
-
12
- @field_count = stream.read_network_int16
13
- @field_count.times do |field_index|
14
- size = stream.read_network_int32
15
- @fields << (size == -1 ? nil : stream.readn(size))
8
+ def initialize(data)
9
+ @values = []
10
+ field_count = data.unpack('n').first
11
+ pos = 2
12
+ field_count.times do |field_index|
13
+ size = data.unpack("@#{pos}N").first
14
+ @values << (size == -1 ? nil : data.unpack("@#{pos + 4}a#{size}").first)
15
+ pos += 4 + [size, 0].max
16
16
  end
17
17
  end
18
18
  end
@@ -1,7 +1,7 @@
1
1
  module Vertica
2
2
  module Messages
3
3
  class EmptyQueryResponse < BackendMessage
4
- message_id ?I
4
+ message_id 'I'
5
5
  end
6
6
  end
7
7
  end
@@ -1,35 +1,7 @@
1
1
  module Vertica
2
2
  module Messages
3
- class ErrorResponse < BackendMessage
4
- message_id ?E
5
-
6
- ERRORS = {
7
- ?q => [0, "Internal Query"],
8
- ?S => [1, "Severity"],
9
- ?M => [2, "Message"],
10
- ?C => [3, "Sqlstate"],
11
- ?D => [4, "Detail"],
12
- ?H => [5, "Hint"],
13
- ?P => [6, "Position"],
14
- ?W => [7, "Where"],
15
- ?p => [8, "Internal Position"],
16
- ?R => [10, "Routine"],
17
- ?F => [11, "File"],
18
- ?L => [12, "Line"],
19
- }
20
-
21
- def initialize(stream, size)
22
- super
23
- @errors, type = {}, nil
24
- @errors[type] = stream.read_cstring while (type = stream.read_byte) != 0
25
- end
26
-
27
- def error
28
- @errors.map { |type, msg| [(ERRORS[type] || [ERRORS.size, type.to_s]), msg].flatten }.
29
- sort_by { |e| e.first }.
30
- map { |e| "#{e[1]}: #{e[2]}" }.
31
- join(', ')
32
- end
3
+ class ErrorResponse < NoticeResponse
4
+ message_id 'E'
33
5
  end
34
6
  end
35
7
  end