vertica 0.7.4

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 (48) hide show
  1. data/LICENSE +19 -0
  2. data/README.textile +69 -0
  3. data/Rakefile +44 -0
  4. data/lib/vertica/bit_helper.rb +51 -0
  5. data/lib/vertica/column.rb +68 -0
  6. data/lib/vertica/connection.rb +247 -0
  7. data/lib/vertica/messages/authentication.rb +33 -0
  8. data/lib/vertica/messages/backend_key_data.rb +16 -0
  9. data/lib/vertica/messages/bind.rb +36 -0
  10. data/lib/vertica/messages/bind_complete.rb +8 -0
  11. data/lib/vertica/messages/cancel_request.rb +25 -0
  12. data/lib/vertica/messages/close.rb +30 -0
  13. data/lib/vertica/messages/close_complete.rb +8 -0
  14. data/lib/vertica/messages/command_complete.rb +16 -0
  15. data/lib/vertica/messages/data_row.rb +23 -0
  16. data/lib/vertica/messages/describe.rb +30 -0
  17. data/lib/vertica/messages/empty_query_response.rb +8 -0
  18. data/lib/vertica/messages/error_response.rb +59 -0
  19. data/lib/vertica/messages/execute.rb +24 -0
  20. data/lib/vertica/messages/flush.rb +15 -0
  21. data/lib/vertica/messages/message.rb +85 -0
  22. data/lib/vertica/messages/no_data.rb +8 -0
  23. data/lib/vertica/messages/notice_response.rb +21 -0
  24. data/lib/vertica/messages/notification_response.rb +18 -0
  25. data/lib/vertica/messages/parameter_description.rb +19 -0
  26. data/lib/vertica/messages/parameter_status.rb +17 -0
  27. data/lib/vertica/messages/parse.rb +31 -0
  28. data/lib/vertica/messages/parse_complete.rb +8 -0
  29. data/lib/vertica/messages/password.rb +33 -0
  30. data/lib/vertica/messages/portal_suspended.rb +8 -0
  31. data/lib/vertica/messages/query.rb +20 -0
  32. data/lib/vertica/messages/ready_for_query.rb +14 -0
  33. data/lib/vertica/messages/row_description.rb +29 -0
  34. data/lib/vertica/messages/ssl_request.rb +14 -0
  35. data/lib/vertica/messages/startup.rb +38 -0
  36. data/lib/vertica/messages/sync.rb +15 -0
  37. data/lib/vertica/messages/terminate.rb +14 -0
  38. data/lib/vertica/messages/unknown.rb +11 -0
  39. data/lib/vertica/notice.rb +11 -0
  40. data/lib/vertica/notification.rb +13 -0
  41. data/lib/vertica/result.rb +28 -0
  42. data/lib/vertica/vertica_socket.rb +8 -0
  43. data/lib/vertica.rb +19 -0
  44. data/test/connection_test.rb +191 -0
  45. data/test/create_schema.sql +4 -0
  46. data/test/test_helper.rb +25 -0
  47. data/vertica.gemspec +64 -0
  48. metadata +112 -0
@@ -0,0 +1,23 @@
1
+ module Vertica
2
+ module Messages
3
+ class DataRow < BackendMessage
4
+ message_id ?D
5
+
6
+ attr_reader :field_count
7
+ attr_reader :fields
8
+
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 << {
16
+ :size => size,
17
+ :value => size == -1 ? nil : stream.readn(size)
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ module Vertica
2
+ module Messages
3
+ class Describe < FrontendMessage
4
+ message_id ?D
5
+
6
+ def initialize(describe_type, describe_name)
7
+ if describe_type == :portal
8
+ @describe_type = ?P
9
+ elsif describe_type == :prepared_statement
10
+ @describe_type = ?S
11
+ else
12
+ raise ArgumentError.new("#{describe_type} is not a valid describe_type. Must be either :portal or :prepared_statement.")
13
+ end
14
+ @describe_name = describe_name
15
+ end
16
+
17
+ def to_bytes(stream)
18
+ size = LENGTH_SIZE
19
+ size += 1
20
+ size += @describe_name.length + 1
21
+
22
+ stream.write_byte(message_id)
23
+ stream.write_network_int32(size) # size
24
+ stream.write_byte(@describe_type)
25
+ stream.write_cstring(@describe_name)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ module Vertica
2
+ module Messages
3
+ class EmptyQueryResponse < BackendMessage
4
+ message_id ?I
5
+
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,59 @@
1
+ module Vertica
2
+ module Messages
3
+ class ErrorResponse < BackendMessage
4
+ message_id ?E
5
+
6
+ def initialize(stream, size)
7
+ super
8
+ @errors = {}
9
+
10
+ field_type = stream.read_byte
11
+ while field_type != 0
12
+ @errors[field_type] = stream.read_cstring
13
+ field_type = stream.read_byte
14
+ end
15
+ end
16
+
17
+ def error
18
+ s = []
19
+ @errors.each do |field_type, message|
20
+ s << [convert_field_type_to_string(field_type), message].flatten
21
+ end
22
+ s.sort_by { |e| e.first }.map { |e| "#{e[1]}: #{e[2]}" }.join(', ')
23
+ end
24
+
25
+ protected
26
+
27
+ def convert_field_type_to_string(field_type)
28
+ case field_type
29
+ when ?S
30
+ [1, "Severity"]
31
+ when ?C
32
+ [3, "Sqlstate"]
33
+ when ?M
34
+ [2, "Message"]
35
+ when ?D
36
+ [4, "Detail"]
37
+ when ?H
38
+ [5, "Hint"]
39
+ when ?P
40
+ [6, "Position"]
41
+ when ?p
42
+ [8, "Internal Position"]
43
+ when ?q
44
+ [0, "Internal Query"]
45
+ when ?W
46
+ [7, "Where"]
47
+ when ?F
48
+ [11, "File"]
49
+ when ?L
50
+ [12, "Line"]
51
+ when ?R
52
+ [10, "Routine"]
53
+ else
54
+ [13, field_type.to_s]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,24 @@
1
+ module Vertica
2
+ module Messages
3
+ class Execute < FrontendMessage
4
+ message_id ?E
5
+
6
+ def initialize(portal_name, max_rows)
7
+ @portal_name = portal_name
8
+ @max_rows = max_rows
9
+ end
10
+
11
+ def to_bytes(stream)
12
+ size = LENGTH_SIZE
13
+ size += @portal_name.length + 1
14
+ size += 4
15
+
16
+ stream.write_byte(message_id)
17
+ stream.write_network_int32(size) # size
18
+ stream.write_cstring(@portal_name)
19
+ stream.write_network_int32(@max_rows)
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module Vertica
2
+ module Messages
3
+ class Flush < FrontendMessage
4
+ message_id ?H
5
+
6
+ def to_bytes(stream)
7
+ size = LENGTH_SIZE
8
+
9
+ stream.write_byte(message_id)
10
+ stream.write_network_int32(size) # size
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,85 @@
1
+ module Vertica
2
+ module Messages
3
+ class Message
4
+ LENGTH_SIZE = 4
5
+
6
+ class << self
7
+
8
+ def message_id(message_id)
9
+ self.const_set(:MESSAGE_ID, message_id)
10
+ class_eval "def message_id; MESSAGE_ID end"
11
+ end
12
+
13
+ end
14
+ end
15
+
16
+ class BackendMessage < Message
17
+ MessageIdMap = {}
18
+
19
+ attr_reader :size
20
+
21
+ class << self
22
+ def message_id(message_id)
23
+ super
24
+ MessageIdMap[message_id.chr] = self
25
+ end
26
+
27
+ def read(stream)
28
+ type = stream.read_byte
29
+ size = stream.read_network_int32
30
+
31
+ raise Vertica::Error::MessageError.new("Bad message size: #{size}") unless size >= 4
32
+
33
+ message_klass = MessageIdMap[type.chr]
34
+ if message_klass.nil?
35
+ Messages::Unknown.new(type)
36
+ else
37
+ message_klass.new(stream, size)
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ def initialize(stream, size)
44
+ @size = size
45
+ end
46
+
47
+ end
48
+
49
+ class FrontendMessage < Message
50
+ end
51
+
52
+ end
53
+ end
54
+
55
+ require 'vertica/messages/unknown'
56
+ require 'vertica/messages/error_response'
57
+ require 'vertica/messages/startup'
58
+ require 'vertica/messages/authentication'
59
+ require 'vertica/messages/password'
60
+ require 'vertica/messages/parameter_status'
61
+ require 'vertica/messages/backend_key_data'
62
+ require 'vertica/messages/ready_for_query'
63
+ require 'vertica/messages/terminate'
64
+ require 'vertica/messages/notification_response'
65
+ require 'vertica/messages/query'
66
+ require 'vertica/messages/notice_response'
67
+ require 'vertica/messages/row_description'
68
+ require 'vertica/messages/command_complete'
69
+ require 'vertica/messages/data_row'
70
+ require 'vertica/messages/empty_query_response'
71
+ require 'vertica/messages/sync'
72
+ require 'vertica/messages/ssl_request'
73
+ require 'vertica/messages/parse'
74
+ require 'vertica/messages/parse_complete'
75
+ require 'vertica/messages/bind'
76
+ require 'vertica/messages/bind_complete'
77
+ require 'vertica/messages/describe'
78
+ require 'vertica/messages/flush'
79
+ require 'vertica/messages/parameter_description'
80
+ require 'vertica/messages/no_data'
81
+ require 'vertica/messages/execute'
82
+ require 'vertica/messages/close'
83
+ require 'vertica/messages/close_complete'
84
+ require 'vertica/messages/portal_suspended'
85
+ require 'vertica/messages/cancel_request'
@@ -0,0 +1,8 @@
1
+ module Vertica
2
+ module Messages
3
+ class NoData < BackendMessage
4
+ message_id ?n
5
+
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ module Vertica
2
+ module Messages
3
+ class NoticeResponse < BackendMessage
4
+ message_id ?N
5
+
6
+ attr_reader :notices
7
+
8
+ def initialize(stream, size)
9
+ super
10
+ @notices = []
11
+
12
+ field_type = stream.read_byte
13
+ while field_type != 0
14
+ @notices << [field_type, stream.read_cstring]
15
+ field_type = stream.read_byte
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ module Vertica
2
+ module Messages
3
+ class NotificationResponse < BackendMessage
4
+ message_id ?A
5
+
6
+ attr_reader :pid
7
+ attr_reader :condition
8
+ attr_reader :addition_info
9
+
10
+ def initialize(stream, size)
11
+ super
12
+ @pid = stream.read_network_int32
13
+ @condition = stream.read_cstring
14
+ @addition_info = stream.read_cstring
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module Vertica
2
+ module Messages
3
+ class ParameterDescription < BackendMessage
4
+ message_id ?t
5
+
6
+ attr_reader :parameter_count
7
+ attr_reader :parameter_types
8
+
9
+ def initialize(stream, size)
10
+ super
11
+ @parameter_types = []
12
+ @parameter_count = stream.read_network_int16
13
+ @parameter_count.times do
14
+ @parameter_types << Vertica::Column::DATA_TYPES[stream.read_network_int32]
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Vertica
2
+ module Messages
3
+ class ParameterStatus < BackendMessage
4
+ message_id ?S
5
+
6
+ attr_reader :name
7
+ attr_reader :value
8
+
9
+ def initialize(stream, size)
10
+ super
11
+ @name = stream.read_cstring
12
+ @value = stream.read_cstring
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ module Vertica
2
+ module Messages
3
+ class Parse < FrontendMessage
4
+ message_id ?P
5
+
6
+ def initialize(name, query, param_types)
7
+ @name = name
8
+ @query = query
9
+ @param_types = param_types
10
+ end
11
+
12
+ def to_bytes(stream)
13
+ size = LENGTH_SIZE
14
+ size += @name.length + 1
15
+ size += @query.length + 1
16
+ size += 2
17
+ size += (@param_types.length * 4)
18
+
19
+ stream.write_byte(message_id)
20
+ stream.write_network_int32(size) # size
21
+ stream.write_cstring(@name)
22
+ stream.write_cstring(@query)
23
+ stream.write_network_int16(@param_types.length)
24
+ @param_types.each do |param_type|
25
+ stream.write_network_int32(param_type)
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ module Vertica
2
+ module Messages
3
+ class ParseComplete < BackendMessage
4
+ message_id ?1
5
+
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,33 @@
1
+ require 'digest/md5'
2
+
3
+ module Vertica
4
+ module Messages
5
+ class Password < FrontendMessage
6
+ message_id ?p
7
+
8
+ def initialize(password, authentication_method = Messages::Authentication::CLEARTEXT_PASSWORD, options = {})
9
+ case authentication_method
10
+ when Messages::Authentication::CLEARTEXT_PASSWORD
11
+ @password = password
12
+ when Messages::Authentication::CRYPT_PASSWORD
13
+ @password = password.crypt(options[:salt])
14
+ when Messages::Authentication::MD5_PASSWORD
15
+ @password = Digest::MD5.hexdigest(password + options[:user])
16
+ @password = Digest::MD5.hexdigest(m + options[:salt])
17
+ @password = 'md5' + @password
18
+ else
19
+ raise ArgumentError.new("unsupported authentication method: #{authentication_method}")
20
+ end
21
+ end
22
+
23
+ def to_bytes(stream)
24
+ size = LENGTH_SIZE
25
+ size += @password.length + 1
26
+ stream.write_byte(message_id)
27
+ stream.write_network_int32(size) # size
28
+ stream.write_cstring(@password)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,8 @@
1
+ module Vertica
2
+ module Messages
3
+ class PortalSuspended < BackendMessage
4
+ message_id ?s
5
+
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ module Vertica
2
+ module Messages
3
+ class Query < FrontendMessage
4
+ message_id ?Q
5
+
6
+ def initialize(query_string)
7
+ @query_string = query_string
8
+ end
9
+
10
+ def to_bytes(stream)
11
+ size = LENGTH_SIZE
12
+ size += @query_string.length + 1
13
+ stream.write_byte(message_id.bytes.first)
14
+ stream.write_network_int32(size) # size
15
+ stream.write_cstring(@query_string)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module Vertica
2
+ module Messages
3
+ class ReadyForQuery < BackendMessage
4
+ message_id ?Z
5
+
6
+ attr_reader :transaction_status
7
+
8
+ def initialize(stream, size)
9
+ super
10
+ @transaction_status = stream.read_byte
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ module Vertica
2
+ module Messages
3
+ class RowDescription < BackendMessage
4
+ message_id ?T
5
+
6
+ attr_reader :field_count
7
+ attr_reader :fields
8
+
9
+ def initialize(stream, size)
10
+ super
11
+
12
+ @fields = []
13
+
14
+ @field_count = stream.read_network_int16
15
+ @field_count.times do |field_index|
16
+ @fields << {
17
+ :name => stream.read_cstring,
18
+ :table_oid => stream.read_network_int32,
19
+ :attribute_number => stream.read_network_int16,
20
+ :data_type_oid => stream.read_network_int32,
21
+ :data_type_size => stream.read_network_int16,
22
+ :type_modifier => stream.read_network_int32,
23
+ :format_code => stream.read_network_int16
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ module Vertica
2
+ module Messages
3
+ class SslRequest < FrontendMessage
4
+ message_id nil
5
+
6
+ def to_bytes(stream)
7
+ size = LENGTH_SIZE + 4
8
+ stream.write_network_int32(size) # size
9
+ stream.write_network_int32(80877103) # size
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ module Vertica
2
+ module Messages
3
+ class Startup < FrontendMessage
4
+ message_id nil
5
+
6
+ def initialize(user, database, options = nil)
7
+ @user = user
8
+ @database = database
9
+ @options = options
10
+ end
11
+
12
+ def to_bytes(stream)
13
+ size = LENGTH_SIZE + 4 # length + protocol
14
+ size += @user.length + 4 + 2 if @user
15
+ size += @database.length + 8 + 2 if @database
16
+ size += @options.length + 7 + 2 if @options
17
+ size += 1 # ending zero
18
+
19
+ stream.write_network_int32(size) # size
20
+ stream.write_network_int32(Vertica::PROTOCOL_VERSION) # proto version
21
+ if @user
22
+ stream.write_cstring('user')
23
+ stream.write_cstring(@user)
24
+ end
25
+ if @database
26
+ stream.write_cstring('database')
27
+ stream.write_cstring(@database)
28
+ end
29
+ if @options
30
+ stream.write_cstring('options')
31
+ stream.write_cstring(@options)
32
+ end
33
+ stream.write_byte(0)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ module Vertica
2
+ module Messages
3
+ class Sync < FrontendMessage
4
+ message_id ?S
5
+
6
+ def to_bytes(stream)
7
+ size = LENGTH_SIZE
8
+
9
+ stream.write_byte(message_id)
10
+ stream.write_network_int32(size)
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Vertica
2
+ module Messages
3
+ class Terminate < FrontendMessage
4
+ message_id ?X
5
+
6
+ def to_bytes(stream)
7
+ size = LENGTH_SIZE
8
+ stream.write_byte(message_id.bytes.first)
9
+ stream.write_network_int32(size)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module Vertica
2
+ module Messages
3
+ class Unknown < BackendMessage
4
+ attr_reader :message_id
5
+
6
+ def initialize(message_id)
7
+ @message_id = message_id
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Vertica
2
+ class Notice
3
+ attr_reader :field_type
4
+ attr_reader :field_value
5
+
6
+ def initialize(field_type, field_value)
7
+ @field_type = field_type
8
+ @field_value = field_value
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Vertica
2
+ class Notification
3
+ attr_reader :process_pid
4
+ attr_reader :condition
5
+ attr_reader :additional_info
6
+
7
+ def initialize(process_pid, condition, additional_info)
8
+ @process_pid = process_pid
9
+ @condition = condition
10
+ @additional_info = additional_info
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ module Vertica
2
+ class Result
3
+
4
+ def initialize(field_descriptions, field_values)
5
+ @field_descriptions = field_descriptions
6
+ @field_values = field_values
7
+ end
8
+
9
+ def row_count
10
+ @row_count ||= @field_values.length
11
+ end
12
+
13
+ def columns
14
+ @columns ||= @field_descriptions.map { |fd| Column.new(fd[:type_modifier], fd[:format_code], fd[:table_oid], fd[:name], fd[:attribute_number], fd[:data_type_oid], fd[:data_type_size]) }
15
+ end
16
+
17
+ def rows
18
+ @field_values.map do |fv|
19
+ index = 0
20
+ fv.map do |f|
21
+ index += 1
22
+ self.columns[index-1].convert(f[:value])
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ require 'socket'
2
+ require 'vertica/bit_helper'
3
+
4
+ module Vertica
5
+ class VerticaSocket < TCPSocket
6
+ include BitHelper
7
+ end
8
+ end
data/lib/vertica.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Vertica
2
+
3
+ class Error < StandardError
4
+
5
+ class ConnectionError < Error; end
6
+
7
+ class MessageError < Error; end
8
+
9
+ end
10
+
11
+ PROTOCOL_VERSION = 3 << 16
12
+
13
+ VERSION = "0.7.3"
14
+
15
+ end
16
+
17
+ require 'vertica/column'
18
+ require 'vertica/result'
19
+ require 'vertica/connection'