vertica 0.8.1 → 0.9.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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