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.
- 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
|