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.
- data/Gemfile +2 -0
- data/Gemfile.lock +23 -0
- data/Rakefile +12 -9
- data/VERSION +1 -1
- data/lib/vertica.rb +2 -7
- data/lib/vertica/connection.rb +47 -57
- data/lib/vertica/messages/backend_messages/authentication.rb +6 -7
- data/lib/vertica/messages/backend_messages/backend_key_data.rb +4 -7
- data/lib/vertica/messages/backend_messages/bind_complete.rb +1 -1
- data/lib/vertica/messages/backend_messages/close_complete.rb +1 -1
- data/lib/vertica/messages/backend_messages/command_complete.rb +13 -7
- data/lib/vertica/messages/backend_messages/data_row.rb +10 -10
- data/lib/vertica/messages/backend_messages/empty_query_response.rb +1 -1
- data/lib/vertica/messages/backend_messages/error_response.rb +2 -30
- data/lib/vertica/messages/backend_messages/no_data.rb +1 -1
- data/lib/vertica/messages/backend_messages/notice_response.rb +33 -7
- data/lib/vertica/messages/backend_messages/notification_response.rb +4 -9
- data/lib/vertica/messages/backend_messages/parameter_description.rb +5 -9
- data/lib/vertica/messages/backend_messages/parameter_status.rb +4 -8
- data/lib/vertica/messages/backend_messages/parse_complete.rb +1 -1
- data/lib/vertica/messages/backend_messages/portal_suspended.rb +1 -1
- data/lib/vertica/messages/backend_messages/ready_for_query.rb +3 -4
- data/lib/vertica/messages/backend_messages/row_description.rb +15 -15
- data/lib/vertica/messages/backend_messages/unknown.rb +2 -2
- data/lib/vertica/messages/frontend_messages/bind.rb +3 -12
- data/lib/vertica/messages/frontend_messages/cancel_request.rb +1 -5
- data/lib/vertica/messages/frontend_messages/close.rb +5 -9
- data/lib/vertica/messages/frontend_messages/describe.rb +5 -9
- data/lib/vertica/messages/frontend_messages/execute.rb +2 -6
- data/lib/vertica/messages/frontend_messages/flush.rb +1 -1
- data/lib/vertica/messages/frontend_messages/parse.rb +2 -8
- data/lib/vertica/messages/frontend_messages/password.rb +5 -6
- data/lib/vertica/messages/frontend_messages/query.rb +2 -3
- data/lib/vertica/messages/frontend_messages/ssl_request.rb +1 -2
- data/lib/vertica/messages/frontend_messages/startup.rb +6 -8
- data/lib/vertica/messages/frontend_messages/sync.rb +1 -1
- data/lib/vertica/messages/frontend_messages/terminate.rb +1 -1
- data/lib/vertica/messages/message.rb +9 -18
- data/lib/vertica/result.rb +8 -1
- data/test/functional/connection_test.rb +73 -0
- data/test/functional/query_test.rb +150 -0
- data/test/test_helper.rb +7 -13
- data/test/unit/backend_message_test.rb +106 -0
- metadata +72 -20
- data/lib/vertica/bit_helper.rb +0 -34
- data/lib/vertica/core_ext/numeric.rb +0 -13
- data/lib/vertica/core_ext/string.rb +0 -22
- data/lib/vertica/notice.rb +0 -11
- data/lib/vertica/vertica_socket.rb +0 -19
- data/test/connection_test.rb +0 -191
data/Gemfile
ADDED
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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.
|
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
|
data/lib/vertica/connection.rb
CHANGED
@@ -1,37 +1,42 @@
|
|
1
1
|
module Vertica
|
2
|
-
|
3
2
|
class Connection
|
4
3
|
|
5
4
|
STATUSES = {
|
6
|
-
|
7
|
-
|
8
|
-
|
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.
|
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
|
-
|
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
|
30
|
-
@
|
31
|
-
conn =
|
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.
|
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
|
-
@
|
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
|
-
|
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
|
-
|
61
|
-
@
|
62
|
-
rescue Errno::ENOTCONN # the backend closed the
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
@
|
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
|
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(
|
21
|
-
|
22
|
-
case @code
|
23
|
-
|
24
|
-
|
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
|
4
|
+
message_id 'K'
|
5
5
|
|
6
|
-
attr_reader :pid
|
7
|
-
attr_reader :key
|
6
|
+
attr_reader :pid, :key
|
8
7
|
|
9
|
-
def initialize(
|
10
|
-
|
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,16 +1,22 @@
|
|
1
1
|
module Vertica
|
2
2
|
module Messages
|
3
3
|
class CommandComplete < BackendMessage
|
4
|
-
message_id
|
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(
|
10
|
-
|
11
|
-
|
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
|
4
|
+
message_id 'D'
|
5
5
|
|
6
|
-
attr_reader :
|
7
|
-
attr_reader :fields
|
6
|
+
attr_reader :values
|
8
7
|
|
9
|
-
def initialize(
|
10
|
-
@
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
size =
|
15
|
-
@
|
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,35 +1,7 @@
|
|
1
1
|
module Vertica
|
2
2
|
module Messages
|
3
|
-
class ErrorResponse <
|
4
|
-
message_id
|
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
|