vertica 0.12.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -20
  3. data/lib/vertica.rb +15 -8
  4. data/lib/vertica/column.rb +32 -13
  5. data/lib/vertica/connection.rb +143 -119
  6. data/lib/vertica/{messages/backend_messages → protocol/backend}/authentication.rb +1 -1
  7. data/lib/vertica/{messages/backend_messages → protocol/backend}/backend_key_data.rb +1 -1
  8. data/lib/vertica/{messages/backend_messages → protocol/backend}/bind_complete.rb +1 -1
  9. data/lib/vertica/{messages/backend_messages → protocol/backend}/close_complete.rb +1 -1
  10. data/lib/vertica/{messages/backend_messages → protocol/backend}/command_complete.rb +1 -1
  11. data/lib/vertica/{messages/backend_messages → protocol/backend}/copy_in_response.rb +2 -2
  12. data/lib/vertica/{messages/backend_messages → protocol/backend}/data_row.rb +1 -1
  13. data/lib/vertica/{messages/backend_messages → protocol/backend}/empty_query_response.rb +1 -1
  14. data/lib/vertica/{messages/backend_messages → protocol/backend}/error_response.rb +1 -1
  15. data/lib/vertica/{messages/backend_messages → protocol/backend}/no_data.rb +1 -1
  16. data/lib/vertica/{messages/backend_messages → protocol/backend}/notice_response.rb +6 -6
  17. data/lib/vertica/{messages/backend_messages → protocol/backend}/parameter_description.rb +2 -2
  18. data/lib/vertica/{messages/backend_messages → protocol/backend}/parameter_status.rb +3 -3
  19. data/lib/vertica/{messages/backend_messages → protocol/backend}/parse_complete.rb +1 -1
  20. data/lib/vertica/{messages/backend_messages → protocol/backend}/portal_suspended.rb +1 -1
  21. data/lib/vertica/{messages/backend_messages → protocol/backend}/ready_for_query.rb +2 -2
  22. data/lib/vertica/{messages/backend_messages → protocol/backend}/row_description.rb +9 -9
  23. data/lib/vertica/{messages/backend_messages → protocol/backend}/unknown.rb +1 -1
  24. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/bind.rb +2 -3
  25. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/cancel_request.rb +3 -4
  26. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/close.rb +3 -3
  27. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/copy_data.rb +6 -6
  28. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/copy_done.rb +1 -1
  29. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/copy_fail.rb +5 -5
  30. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/describe.rb +3 -3
  31. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/execute.rb +3 -3
  32. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/flush.rb +1 -1
  33. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/parse.rb +3 -3
  34. data/lib/vertica/protocol/frontend/password.rb +32 -0
  35. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/query.rb +3 -4
  36. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/ssl_request.rb +2 -2
  37. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/startup.rb +2 -3
  38. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/sync.rb +1 -1
  39. data/lib/vertica/{messages/frontend_messages → protocol/frontend}/terminate.rb +1 -1
  40. data/lib/vertica/protocol/message.rb +86 -0
  41. data/lib/vertica/query.rb +63 -44
  42. data/lib/vertica/result.rb +25 -47
  43. data/lib/vertica/row.rb +44 -0
  44. data/lib/vertica/row_description.rb +102 -0
  45. data/lib/vertica/version.rb +1 -1
  46. data/test/connection.yml.example +8 -8
  47. data/test/functional/functional_connection_test.rb +178 -0
  48. data/test/functional/{query_test.rb → functional_query_test.rb} +64 -67
  49. data/test/functional/functional_value_conversion_test.rb +117 -0
  50. data/test/test_helper.rb +1 -1
  51. data/test/unit/backend_message_test.rb +57 -57
  52. data/test/unit/column_test.rb +23 -23
  53. data/test/unit/frontend_message_test.rb +5 -5
  54. data/test/unit/quoting_test.rb +16 -5
  55. data/test/unit/result_test.rb +61 -0
  56. data/test/unit/row_description_test.rb +56 -0
  57. data/test/unit/row_test.rb +31 -0
  58. data/vertica.gemspec +1 -0
  59. metadata +67 -45
  60. data/lib/vertica/messages/frontend_messages/password.rb +0 -34
  61. data/lib/vertica/messages/message.rb +0 -79
  62. data/test/functional/connection_test.rb +0 -128
  63. data/test/functional/value_conversion_test.rb +0 -88
@@ -1,5 +1,5 @@
1
1
  module Vertica
2
- module Messages
2
+ module Protocol
3
3
 
4
4
  class Startup < FrontendMessage
5
5
  message_id nil
@@ -10,13 +10,12 @@ module Vertica
10
10
  @options = options
11
11
  end
12
12
 
13
- def to_bytes
13
+ def message_body
14
14
  str = [Vertica::PROTOCOL_VERSION].pack('N')
15
15
  str << ["user", @user].pack('Z*Z*') if @user
16
16
  str << ["database", @database].pack('Z*Z*') if @database
17
17
  str << ["options", @options].pack('Z*Z*') if @options
18
18
  str << [].pack('x')
19
- message_string str
20
19
  end
21
20
  end
22
21
  end
@@ -1,5 +1,5 @@
1
1
  module Vertica
2
- module Messages
2
+ module Protocol
3
3
  class Sync < FrontendMessage
4
4
  message_id 'S'
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module Vertica
2
- module Messages
2
+ module Protocol
3
3
  class Terminate < FrontendMessage
4
4
  message_id 'X'
5
5
  end
@@ -0,0 +1,86 @@
1
+ module Vertica
2
+ module Protocol
3
+
4
+ class Message
5
+ def self.message_id(message_id)
6
+ self.send(:define_method, :message_id) { message_id }
7
+ end
8
+ end
9
+
10
+ class BackendMessage < Message
11
+ MessageIdMap = {}
12
+
13
+ def self.factory(type, data)
14
+ if klass = MessageIdMap[type]
15
+ klass.new(data)
16
+ else
17
+ Protocol::Unknown.new(type, data)
18
+ end
19
+ end
20
+
21
+ def self.message_id(message_id)
22
+ super
23
+ MessageIdMap[message_id] = self
24
+ end
25
+
26
+ def initialize(_data)
27
+ end
28
+ end
29
+
30
+ class FrontendMessage < Message
31
+ def to_bytes
32
+ prepend_message_header(message_body)
33
+ end
34
+
35
+ protected
36
+
37
+ def message_body
38
+ ''
39
+ end
40
+
41
+ def prepend_message_header(msg)
42
+ if message_id
43
+ [message_id, 4 + msg.bytesize, msg].pack('aNa*')
44
+ else
45
+ [4 + msg.bytesize, msg].pack('Na*')
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ require 'vertica/protocol/backend/authentication'
53
+ require 'vertica/protocol/backend/backend_key_data'
54
+ require 'vertica/protocol/backend/bind_complete'
55
+ require 'vertica/protocol/backend/close_complete'
56
+ require 'vertica/protocol/backend/command_complete'
57
+ require 'vertica/protocol/backend/data_row'
58
+ require 'vertica/protocol/backend/empty_query_response'
59
+ require 'vertica/protocol/backend/notice_response'
60
+ require 'vertica/protocol/backend/error_response'
61
+ require 'vertica/protocol/backend/no_data'
62
+ require 'vertica/protocol/backend/parameter_description'
63
+ require 'vertica/protocol/backend/parameter_status'
64
+ require 'vertica/protocol/backend/parse_complete'
65
+ require 'vertica/protocol/backend/portal_suspended'
66
+ require 'vertica/protocol/backend/ready_for_query'
67
+ require 'vertica/protocol/backend/row_description'
68
+ require 'vertica/protocol/backend/copy_in_response'
69
+ require 'vertica/protocol/backend/unknown'
70
+
71
+ require 'vertica/protocol/frontend/bind'
72
+ require 'vertica/protocol/frontend/cancel_request'
73
+ require 'vertica/protocol/frontend/close'
74
+ require 'vertica/protocol/frontend/describe'
75
+ require 'vertica/protocol/frontend/execute'
76
+ require 'vertica/protocol/frontend/flush'
77
+ require 'vertica/protocol/frontend/parse'
78
+ require 'vertica/protocol/frontend/password'
79
+ require 'vertica/protocol/frontend/query'
80
+ require 'vertica/protocol/frontend/ssl_request'
81
+ require 'vertica/protocol/frontend/startup'
82
+ require 'vertica/protocol/frontend/sync'
83
+ require 'vertica/protocol/frontend/terminate'
84
+ require 'vertica/protocol/frontend/copy_done'
85
+ require 'vertica/protocol/frontend/copy_fail'
86
+ require 'vertica/protocol/frontend/copy_data'
@@ -1,85 +1,104 @@
1
1
  class Vertica::Query
2
2
 
3
- attr_reader :connection, :sql, :result, :error
4
- attr_accessor :row_handler, :copy_handler, :row_style
3
+ attr_reader :connection, :sql, :error, :result
5
4
 
6
- def initialize(connection, sql, options = {})
5
+ def initialize(connection, sql, row_handler: nil, copy_handler: nil)
7
6
  @connection, @sql = connection, sql
8
-
9
- @row_style = options[:row_style] || @connection.row_style || :hash
10
- @row_handler = options[:row_handler]
11
- @copy_handler = options[:copy_handler]
12
-
13
- @error = nil
14
- @result = Vertica::Result.new(row_style)
7
+ if row_handler.nil?
8
+ @buffer = []
9
+ @row_handler = lambda { |row| buffer_row(row) }
10
+ else
11
+ @row_handler = row_handler
12
+ end
13
+ @copy_handler = copy_handler
14
+ @error = nil
15
15
  end
16
16
 
17
17
  def run
18
- @connection.write_message(Vertica::Messages::Query.new(sql))
18
+ @connection.write_message(Vertica::Protocol::Query.new(sql))
19
19
 
20
20
  begin
21
21
  process_message(message = @connection.read_message)
22
- end until message.kind_of?(Vertica::Messages::ReadyForQuery)
22
+ end until message.kind_of?(Vertica::Protocol::ReadyForQuery)
23
23
 
24
24
  raise error unless error.nil?
25
25
  return result
26
26
  end
27
27
 
28
- def write(data)
29
- @connection.write_message(Vertica::Messages::CopyData.new(data))
30
- return self
31
- end
32
-
33
- alias_method :<<, :write
34
-
35
28
  def to_s
36
29
  @sql
37
30
  end
38
31
 
39
32
  protected
40
33
 
34
+ def buffer_rows?
35
+ !!@buffer
36
+ end
37
+
41
38
  def process_message(message)
42
39
  case message
43
- when Vertica::Messages::ErrorResponse
40
+ when Vertica::Protocol::ErrorResponse
44
41
  @error = Vertica::Error::QueryError.from_error_response(message, @sql)
45
- when Vertica::Messages::EmptyQueryResponse
42
+ when Vertica::Protocol::EmptyQueryResponse
46
43
  @error = Vertica::Error::EmptyQueryError.new("A SQL string was expected, but the given string was blank or only contained SQL comments.")
47
- when Vertica::Messages::CopyInResponse
48
- handle_copy_from_stdin
49
- when Vertica::Messages::RowDescription
50
- result.descriptions = message
51
- when Vertica::Messages::DataRow
52
- handle_datarow(message)
53
- when Vertica::Messages::CommandComplete
54
- result.tag = message.tag
44
+ when Vertica::Protocol::CopyInResponse
45
+ handle_copy_in_response(message)
46
+ when Vertica::Protocol::RowDescription
47
+ handle_row_description(message)
48
+ when Vertica::Protocol::DataRow
49
+ handle_data_row(message)
50
+ when Vertica::Protocol::CommandComplete
51
+ handle_command_complete(message)
55
52
  else
56
53
  @connection.process_message(message)
57
54
  end
58
55
  end
59
56
 
60
- def handle_copy_from_stdin
61
- if copy_handler.nil?
62
- @connection.write_message(Vertica::Messages::CopyFail.new('no handler provided'))
57
+ def handle_row_description(message)
58
+ @row_description = Vertica::RowDescription.build(message)
59
+ end
60
+
61
+ def handle_data_row(message)
62
+ @row_handler.call(@row_description.build_row(message))
63
+ end
64
+
65
+ def handle_command_complete(message)
66
+ if buffer_rows?
67
+ @result = Vertica::Result.build(row_description: @row_description, rows: @buffer, tag: message.tag)
68
+ @row_description, @buffer = nil, nil
69
+ else
70
+ @result = message.tag
71
+ end
72
+ end
73
+
74
+ def handle_copy_in_response(_message)
75
+ if @copy_handler.nil?
76
+ @connection.write_message(Vertica::Protocol::CopyFail.new('no handler provided'))
63
77
  else
64
78
  begin
65
- if copy_handler.call(self) == :rollback
66
- @connection.write_message(Vertica::Messages::CopyFail.new("rollback"))
67
- else
68
- @connection.write_message(Vertica::Messages::CopyDone.new)
69
- end
79
+ @copy_handler.call(CopyFromStdinWriter.new(connection))
80
+ @connection.write_message(Vertica::Protocol::CopyDone.new)
70
81
  rescue => e
71
- @connection.write_message(Vertica::Messages::CopyFail.new(e.message))
82
+ @connection.write_message(Vertica::Protocol::CopyFail.new(e.message))
72
83
  end
73
84
  end
74
85
  end
75
86
 
76
- def handle_datarow(datarow_message)
77
- record = result.format_row(datarow_message)
78
- result.add_row(record) if buffer_rows?
79
- row_handler.call(record) if row_handler
87
+ def buffer_row(row)
88
+ @buffer << row
80
89
  end
81
90
 
82
- def buffer_rows?
83
- row_handler.nil? && copy_handler.nil?
91
+ class CopyFromStdinWriter
92
+ def initialize(connection)
93
+ @connection = connection
94
+ end
95
+
96
+ def write(data)
97
+ @connection.write_message(Vertica::Protocol::CopyData.new(data))
98
+ return self
99
+ end
100
+
101
+ alias_method :<<, :write
84
102
  end
103
+ private_constant :CopyFromStdinWriter
85
104
  end
@@ -1,70 +1,48 @@
1
1
  class Vertica::Result
2
2
  include Enumerable
3
3
 
4
- attr_reader :columns
4
+ attr_reader :row_description
5
5
  attr_reader :rows
6
- attr_accessor :tag, :notice
6
+ attr_reader :tag
7
7
 
8
- def initialize(row_style = :hash)
9
- @row_style = row_style
10
- @rows = []
8
+ def initialize(row_description: nil, rows: nil, tag: nil)
9
+ @row_description, @rows, @tag = row_description, rows, tag
11
10
  end
12
11
 
13
- def descriptions=(message)
14
- @columns = message.fields.map { |fd| Vertica::Column.new(fd) }
15
- end
16
-
17
- def format_row_as_hash(row_data)
18
- row = {}
19
- row_data.values.each_with_index do |value, idx|
20
- col = columns.fetch(idx)
21
- row[col.name] = col.convert(value)
22
- end
23
- row
12
+ def each(&block)
13
+ @rows.each(&block)
24
14
  end
25
15
 
26
- def format_row(row_data)
27
- send("format_row_as_#{@row_style}", row_data)
16
+ def empty?
17
+ @rows.empty?
28
18
  end
29
19
 
30
- def format_row_as_array(row_data)
31
- row = []
32
- row_data.values.each_with_index do |value, idx|
33
- row << columns.fetch(idx).convert(value)
34
- end
35
- row
20
+ def size
21
+ @rows.length
36
22
  end
37
23
 
38
- def add_row(row)
39
- @rows << row
40
- end
24
+ alias_method :count, :size
25
+ alias_method :length, :size
41
26
 
42
- def each_row(&block)
43
- @rows.each(&block)
27
+ def fetch(row_index, col = nil)
28
+ row = rows.fetch(row_index)
29
+ return row if col.nil?
30
+ row.fetch(col)
44
31
  end
45
32
 
46
- def empty?
47
- @rows.empty?
48
- end
33
+ alias_method :[], :fetch
49
34
 
50
- def the_value
51
- if empty?
52
- nil
53
- else
54
- @row_style == :array ? rows[0][0] : rows[0][columns[0].name]
55
- end
35
+ def value
36
+ fetch(0, 0)
56
37
  end
57
38
 
58
- def [](row, col = nil)
59
- col.nil? ? row[row] : rows[row][col]
60
- end
39
+ alias_method :the_value, :value
61
40
 
62
- alias_method :each, :each_row
41
+ alias_method :columns, :row_description
63
42
 
64
- def row_count
65
- @rows.size
43
+ def self.build(row_description: nil, rows: [], tag: nil)
44
+ row_description = Vertica::RowDescription.build(row_description)
45
+ rows = rows.map { |values| row_description.build_row(values) }
46
+ new(row_description: row_description, rows: rows, tag: tag)
66
47
  end
67
-
68
- alias_method :size, :row_count
69
- alias_method :length, :row_count
70
48
  end
@@ -0,0 +1,44 @@
1
+ class Vertica::Row
2
+ include Enumerable
3
+
4
+ def initialize(row_description, values)
5
+ @row_description, @values = row_description, values
6
+ end
7
+
8
+ def each(&block)
9
+ @values.each(&block)
10
+ end
11
+
12
+ def fetch(name_or_index)
13
+ @values.fetch(column_index(name_or_index))
14
+ end
15
+
16
+ def inspect
17
+ "<Vertica::Row#{@values.inspect}>"
18
+ end
19
+
20
+ alias_method :[], :fetch
21
+
22
+ def to_a
23
+ @values.to_a
24
+ end
25
+
26
+ def to_h(symbolize_keys: false)
27
+ @row_description.inject({}) do |carry, column|
28
+ key = symbolize_keys ? column.name.to_sym : column.name
29
+ carry.merge(key => fetch(column.name))
30
+ end
31
+ end
32
+
33
+ def column(name_or_index)
34
+ column_with_index(name_or_index).fetch(0)
35
+ end
36
+
37
+ def column_index(name_or_index)
38
+ column_with_index(name_or_index).fetch(1)
39
+ end
40
+
41
+ def column_with_index(name_or_index)
42
+ @row_description.column_with_index(name_or_index)
43
+ end
44
+ end
@@ -0,0 +1,102 @@
1
+ class Vertica::RowDescription
2
+ include Enumerable
3
+
4
+ def self.build(columns)
5
+ case columns
6
+ when Vertica::Protocol::RowDescription
7
+ new(columns.fields.map { |fd| Vertica::Column.new(fd) })
8
+ when Vertica::RowDescription
9
+ columns
10
+ when Array
11
+ new(columns)
12
+ when nil
13
+ nil
14
+ else
15
+ raise ArgumentError, "Invalid list of columns: #{columns.inspect}"
16
+ end
17
+ end
18
+
19
+ def initialize(columns)
20
+ @columns = columns
21
+ end
22
+
23
+ def column(name_or_index)
24
+ column_with_index(name_or_index).first
25
+ end
26
+
27
+ alias_method :[], :column
28
+
29
+ def column_with_index(name_or_index)
30
+ columns_index.fetch(name_or_index)
31
+ end
32
+
33
+ def each(&block)
34
+ @columns.each(&block)
35
+ end
36
+
37
+ def size
38
+ @columns.size
39
+ end
40
+
41
+ alias_method :length, :size
42
+
43
+ def to_a
44
+ @columns.clone
45
+ end
46
+
47
+ def to_h(symbolize_keys: false)
48
+ @columns.inject({}) do |carry, column|
49
+ key = symbolize_keys ? column.name.to_sym : column.name
50
+ carry.merge(key => column)
51
+ end
52
+ end
53
+
54
+ def build_row(values)
55
+ case values
56
+ when Vertica::Row
57
+ values
58
+
59
+ when Vertica::Protocol::DataRow
60
+ converted_values = @columns.map.with_index do |column, index|
61
+ column.convert(values.values.fetch(index))
62
+ end
63
+ Vertica::Row.new(self, converted_values)
64
+
65
+ when Array
66
+ raise ArgumentError, "Number of values does not match row description" if values.size != size
67
+ Vertica::Row.new(self, values)
68
+
69
+ when Hash
70
+ raise ArgumentError, "Number of values does not match row description" if values.size != size
71
+ values_as_array = @columns.map { |column| values[column.name] || values[column.name.to_sym] }
72
+ Vertica::Row.new(self, values_as_array)
73
+
74
+ else
75
+ raise ArgumentError, "Don't know how to build a row from a #{values.class.name} instance"
76
+ end
77
+ end
78
+
79
+ def eql?(other)
80
+ self.class === other && other.to_a == self.to_a
81
+ end
82
+
83
+ alias_method :==, :eql?
84
+
85
+ def hash
86
+ @columns.hash
87
+ end
88
+
89
+ protected
90
+
91
+ def columns_index
92
+ @columns_index ||= begin
93
+ result = {}
94
+ @columns.each_with_index do |column, index|
95
+ result[index] = [column, index]
96
+ result[column.name.to_s] = [column, index]
97
+ result[column.name.to_sym] = [column, index]
98
+ end
99
+ result
100
+ end
101
+ end
102
+ end