vertica 0.9.0.beta3 → 0.9.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ module Vertica
2
+ module Messages
3
+ class CopyInResponse < BackendMessage
4
+ message_id 'G'
5
+
6
+ def initialize(data)
7
+ values = data.unpack('Cn*')
8
+ @format = values[0]
9
+ @column_formats = values.slice(2..-1)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,12 +1,19 @@
1
1
  module Vertica
2
2
  module Messages
3
3
  class ReadyForQuery < BackendMessage
4
+
5
+ STATUSES = {
6
+ 'I' => :no_transaction,
7
+ 'T' => :in_transaction,
8
+ 'E' => :failed_transaction
9
+ }
10
+
4
11
  message_id 'Z'
5
12
 
6
13
  attr_reader :transaction_status
7
14
 
8
15
  def initialize(data)
9
- @transaction_status = data.unpack('a').first
16
+ @transaction_status = STATUSES[data.unpack('a').first]
10
17
  end
11
18
  end
12
19
  end
@@ -6,12 +6,13 @@ module Vertica
6
6
  def initialize(portal_name, prepared_statement_name, parameter_values)
7
7
  @portal_name = portal_name
8
8
  @prepared_statement_name = prepared_statement_name
9
- @parameter_values = parameter_values.map(&:to_s)
9
+ @parameter_values = parameter_values
10
10
  end
11
11
 
12
12
  def to_bytes
13
13
  bytes = [@portal_name, @prepared_statement_name, 0, @parameter_values.length].pack('Z*Z*nn')
14
- bytes << @parameter_values.map { |val| [val.length, val].pack('Na*') }.join('') << [0].pack('n')
14
+ bytes << @parameter_values.map { |val| val.nil? ? [-1].pack('N') : [val.length, val].pack('Na*') }.join('')
15
+ bytes << [0].pack('n')
15
16
  message_string bytes
16
17
  end
17
18
  end
@@ -0,0 +1,15 @@
1
+ module Vertica
2
+ module Messages
3
+ class CopyData < FrontendMessage
4
+ message_id 'd'
5
+
6
+ def initialize(data)
7
+ @data = data
8
+ end
9
+
10
+ def to_bytes
11
+ message_string [@data].pack('a*')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Vertica
2
+ module Messages
3
+ class CopyDone < FrontendMessage
4
+ message_id 'c'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module Vertica
2
+ module Messages
3
+ class CopyFail < FrontendMessage
4
+ message_id 'f'
5
+
6
+ def initialize(error_message)
7
+ @error_message = error_message
8
+ end
9
+
10
+ def to_bytes
11
+ message_string [@error_message].pack('Z*')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -6,17 +6,17 @@ module Vertica
6
6
 
7
7
  def initialize(password, auth_method = nil, options = {})
8
8
  @password = password
9
- @auth_method = auth_method || Messages::Authentication::CLEARTEXT_PASSWORD
9
+ @auth_method = auth_method || Vertica::Messages::Authentication::CLEARTEXT_PASSWORD
10
10
  @options = options
11
11
  end
12
12
 
13
13
  def encoded_password
14
14
  case @auth_method
15
- when Authentication::CLEARTEXT_PASSWORD
15
+ when Vertica::Messages::Authentication::CLEARTEXT_PASSWORD
16
16
  @password
17
- when Authentication::CRYPT_PASSWORD
17
+ when Vertica::Messages::Authentication::CRYPT_PASSWORD
18
18
  @password.crypt(@options[:salt])
19
- when Authentication::MD5_PASSWORD
19
+ when Vertica::Messages::Authentication::MD5_PASSWORD
20
20
  require 'digest/md5'
21
21
  @password = Digest::MD5.hexdigest(@password + @options[:user])
22
22
  @password = Digest::MD5.hexdigest(@password + @options[:salt])
@@ -53,13 +53,13 @@ require 'vertica/messages/backend_messages/empty_query_response'
53
53
  require 'vertica/messages/backend_messages/notice_response'
54
54
  require 'vertica/messages/backend_messages/error_response'
55
55
  require 'vertica/messages/backend_messages/no_data'
56
- require 'vertica/messages/backend_messages/notification_response'
57
56
  require 'vertica/messages/backend_messages/parameter_description'
58
57
  require 'vertica/messages/backend_messages/parameter_status'
59
58
  require 'vertica/messages/backend_messages/parse_complete'
60
59
  require 'vertica/messages/backend_messages/portal_suspended'
61
60
  require 'vertica/messages/backend_messages/ready_for_query'
62
61
  require 'vertica/messages/backend_messages/row_description'
62
+ require 'vertica/messages/backend_messages/copy_in_response'
63
63
  require 'vertica/messages/backend_messages/unknown'
64
64
 
65
65
  require 'vertica/messages/frontend_messages/bind'
@@ -75,3 +75,6 @@ require 'vertica/messages/frontend_messages/ssl_request'
75
75
  require 'vertica/messages/frontend_messages/startup'
76
76
  require 'vertica/messages/frontend_messages/sync'
77
77
  require 'vertica/messages/frontend_messages/terminate'
78
+ require 'vertica/messages/frontend_messages/copy_done'
79
+ require 'vertica/messages/frontend_messages/copy_fail'
80
+ require 'vertica/messages/frontend_messages/copy_data'
@@ -0,0 +1,84 @@
1
+ class Vertica::Query
2
+
3
+ attr_reader :connection, :sql
4
+ attr_accessor :row_handler, :copy_handler, :row_style
5
+
6
+ def initialize(connection, sql, options = {})
7
+ @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
+ end
13
+
14
+
15
+ def run
16
+ @connection.write Vertica::Messages::Query.new(@sql)
17
+ result, error = nil, nil
18
+ begin
19
+ case message = @connection.read_message
20
+ when Vertica::Messages::ErrorResponse
21
+ error = message.error_message
22
+ when Vertica::Messages::EmptyQueryResponse
23
+ error = "The provided query was empty."
24
+ when Vertica::Messages::CopyInResponse
25
+ handle_copy_from_stdin
26
+ when Vertica::Messages::RowDescription, Vertica::Messages::CommandComplete
27
+ result = retreive_result(message, Vertica::Result.new(row_style))
28
+ else
29
+ @connection.process_message(message)
30
+ end
31
+ end until message.kind_of?(Vertica::Messages::ReadyForQuery)
32
+
33
+ raise Vertica::Error::QueryError, error unless error.nil?
34
+ return result
35
+ end
36
+
37
+ def copy_data(data)
38
+ @connection.write Vertica::Messages::CopyData.new(data)
39
+ return self
40
+ end
41
+
42
+ alias_method :<<, :copy_data
43
+
44
+ protected
45
+
46
+ def handle_copy_from_stdin
47
+ if copy_handler.nil?
48
+ @connection.write Vertica::Messages::CopyFail.new('no handler provided')
49
+ else
50
+ begin
51
+ if copy_handler.call(self) == :rollback
52
+ @connection.write Vertica::Messages::CopyFail.new("rollback")
53
+ else
54
+ @connection.write Vertica::Messages::CopyDone.new
55
+ end
56
+ rescue => e
57
+ @connection.write Vertica::Messages::CopyFail.new(e.message)
58
+ raise
59
+ end
60
+ end
61
+ end
62
+
63
+ def retreive_result(message, result)
64
+ until message.kind_of?(Vertica::Messages::CommandComplete)
65
+ case message
66
+ when Vertica::Messages::RowDescription
67
+ result.descriptions = message
68
+ when Vertica::Messages::DataRow
69
+ record = result.format_row(message)
70
+ result.add_row(record) if buffer_rows?
71
+ @row_handler.call(record) if @row_handler
72
+ else
73
+ @connection.process_message(message)
74
+ end
75
+ message = @connection.read_message
76
+ end
77
+ result.tag = message.tag
78
+ return result
79
+ end
80
+
81
+ def buffer_rows?
82
+ @row_handler.nil? && @copy_handler.nil?
83
+ end
84
+ end
@@ -1,58 +1,54 @@
1
- module Vertica
2
- class Result
3
-
4
- include Enumerable
5
-
6
- attr_reader :columns
7
- attr_reader :rows
8
-
9
- attr_accessor
10
-
11
- def initialize(row_style = :hash)
12
- @row_style = row_style
13
- @rows = []
14
- end
1
+ class Vertica::Result
2
+ include Enumerable
3
+
4
+ attr_reader :columns
5
+ attr_reader :rows
6
+ attr_accessor :tag, :notice
7
+
8
+ def initialize(row_style = :hash)
9
+ @row_style = row_style
10
+ @rows = []
11
+ end
15
12
 
16
- def descriptions=(message)
17
- @columns = message.fields.map { |fd| Column.new(fd) }
18
- end
13
+ def descriptions=(message)
14
+ @columns = message.fields.map { |fd| Vertica::Column.new(fd) }
15
+ end
19
16
 
20
- def format_row_as_hash(row_data)
21
- row = {}
22
- row_data.values.each_with_index do |value, idx|
23
- col = columns[idx]
24
- row[col.name] = col.convert(value)
25
- end
26
- row
27
- end
28
-
29
- def format_row(row_data)
30
- send("format_row_as_#{@row_style}", row_data)
17
+ def format_row_as_hash(row_data)
18
+ row = {}
19
+ row_data.values.each_with_index do |value, idx|
20
+ col = columns[idx]
21
+ row[col.name] = col.convert(value)
31
22
  end
32
-
33
- def format_row_as_array(row_data)
34
- row = []
35
- row_data.values.each_with_index do |value, idx|
36
- row << columns[idx].convert(value)
37
- end
38
- row
39
- end
40
-
41
- def add_row(row_data)
42
- @rows << format_row(row_data)
23
+ row
24
+ end
25
+
26
+ def format_row(row_data)
27
+ send("format_row_as_#{@row_style}", row_data)
28
+ end
29
+
30
+ def format_row_as_array(row_data)
31
+ row = []
32
+ row_data.values.each_with_index do |value, idx|
33
+ row << columns[idx].convert(value)
43
34
  end
35
+ row
36
+ end
44
37
 
45
- def each_row(&block)
46
- @rows.each(&block)
47
- end
48
-
49
- alias_method :each, :each_row
38
+ def add_row(row)
39
+ @rows << row
40
+ end
50
41
 
51
- def row_count
52
- @rows.size
53
- end
42
+ def each_row(&block)
43
+ @rows.each(&block)
44
+ end
45
+
46
+ alias_method :each, :each_row
54
47
 
55
- alias_method :size, :row_count
56
- alias_method :length, :row_count
48
+ def row_count
49
+ @rows.size
57
50
  end
51
+
52
+ alias_method :size, :row_count
53
+ alias_method :length, :row_count
58
54
  end
@@ -5,7 +5,7 @@ class ConnectionTest < Test::Unit::TestCase
5
5
  def teardown
6
6
  @connection.close if @connection
7
7
  end
8
-
8
+
9
9
  def test_new_connection
10
10
  @connection = Vertica::Connection.new(TEST_CONNECTION_HASH)
11
11
 
@@ -19,8 +19,6 @@ class ConnectionTest < Test::Unit::TestCase
19
19
  # parameters
20
20
  assert @connection.parameters.kind_of?(Hash)
21
21
  assert @connection.parameters.include?('server_version')
22
-
23
- assert_equal [], @connection.notifications
24
22
  end
25
23
 
26
24
  def test_close_connection
@@ -67,7 +65,7 @@ class ConnectionTest < Test::Unit::TestCase
67
65
 
68
66
  def test_new_with_error_response
69
67
  assert_raises Vertica::Error::ConnectionError do
70
- Vertica::Connection.new(TEST_CONNECTION_HASH.merge(:database => 'nonexistant_db'))
68
+ Vertica::Connection.new(TEST_CONNECTION_HASH.merge('database' => 'nonexistant_db'))
71
69
  end
72
70
  end
73
71
  end
@@ -4,20 +4,19 @@ class QueryTest < Test::Unit::TestCase
4
4
 
5
5
  def setup
6
6
  @connection = Vertica::Connection.new(TEST_CONNECTION_HASH)
7
- @connection.query("CREATE TABLE IF NOT EXISTS test_table (id int, name varchar(100))")
8
- @connection.query("CREATE PROJECTION IF NOT EXISTS test_table_p (id, name) AS SELECT * FROM test_table SEGMENTED BY HASH(id) ALL NODES OFFSET 1")
9
- @connection.query("INSERT INTO test_table VALUES (1, 'matt')")
7
+ @connection.query("CREATE TABLE IF NOT EXISTS test_ruby_vertica_table (id int, name varchar(100))")
8
+ @connection.query("CREATE PROJECTION IF NOT EXISTS test_ruby_vertica_table_p (id, name) AS SELECT * FROM test_ruby_vertica_table SEGMENTED BY HASH(id) ALL NODES OFFSET 1")
9
+ @connection.query("INSERT INTO test_ruby_vertica_table VALUES (1, 'matt')")
10
10
  @connection.query("COMMIT")
11
11
  end
12
12
 
13
13
  def teardown
14
- @connection.query("DROP TABLE IF EXISTS test_table CASCADE;")
15
- @connection.query("COMMIT")
14
+ @connection.query("DROP TABLE IF EXISTS test_ruby_vertica_table CASCADE;")
16
15
  @connection.close
17
16
  end
18
17
 
19
18
  def test_select_query_with_results_as_hash
20
- r = @connection.query("SELECT * FROM test_table")
19
+ r = @connection.query("SELECT * FROM test_ruby_vertica_table")
21
20
  assert_equal 1, r.row_count
22
21
  assert_equal 2, r.columns.length
23
22
  assert_equal :integer, r.columns[0].data_type
@@ -27,10 +26,10 @@ class QueryTest < Test::Unit::TestCase
27
26
 
28
27
  assert_equal [{:id => 1, :name => "matt"}], r.rows
29
28
  end
30
-
29
+
31
30
  def test_select_query_with_results_as_array
32
31
  @connection.row_style = :array
33
- r = @connection.query("SELECT * FROM test_table")
32
+ r = @connection.query("SELECT * FROM test_ruby_vertica_table")
34
33
  assert_equal 1, r.row_count
35
34
  assert_equal 2, r.columns.length
36
35
  assert_equal :integer, r.columns[0].data_type
@@ -40,10 +39,10 @@ class QueryTest < Test::Unit::TestCase
40
39
 
41
40
  assert_equal [[1, "matt"]], r.rows
42
41
  end
43
-
42
+
44
43
 
45
44
  def test_select_query_with_no_results
46
- r = @connection.query("SELECT * FROM test_table WHERE 1 != 1")
45
+ r = @connection.query("SELECT * FROM test_ruby_vertica_table WHERE 1 != 1")
47
46
  assert_equal 0, r.row_count
48
47
  assert_equal 2, r.columns.length
49
48
  assert_equal :integer, r.columns[0].data_type
@@ -54,7 +53,7 @@ class QueryTest < Test::Unit::TestCase
54
53
  end
55
54
 
56
55
  def test_insert
57
- r = @connection.query("INSERT INTO test_table VALUES (2, 'stefanie')")
56
+ r = @connection.query("INSERT INTO test_ruby_vertica_table VALUES (2, 'stefanie')")
58
57
  assert_equal 1, r.row_count
59
58
  assert_equal 1, r.columns.length
60
59
  assert_equal :integer, r.columns[0].data_type
@@ -64,7 +63,7 @@ class QueryTest < Test::Unit::TestCase
64
63
 
65
64
 
66
65
  def test_delete_of_no_rows
67
- r = @connection.query("DELETE FROM test_table WHERE 1 != 1")
66
+ r = @connection.query("DELETE FROM test_ruby_vertica_table WHERE 1 != 1")
68
67
  assert_equal 1, r.row_count
69
68
  assert_equal 1, r.columns.length
70
69
  assert_equal :integer, r.columns[0].data_type
@@ -73,7 +72,7 @@ class QueryTest < Test::Unit::TestCase
73
72
  end
74
73
 
75
74
  def test_delete_of_a_row
76
- r = @connection.query("DELETE FROM test_table WHERE id = 1")
75
+ r = @connection.query("DELETE FROM test_ruby_vertica_table WHERE id = 1")
77
76
  assert_equal 1, r.row_count
78
77
  assert_equal 1, r.columns.length
79
78
  assert_equal :integer, r.columns[0].data_type
@@ -82,75 +81,20 @@ class QueryTest < Test::Unit::TestCase
82
81
  end
83
82
 
84
83
  def test_empty_query
85
- assert_raises ArgumentError do
84
+ assert_raises Vertica::Error::QueryError do
86
85
  @connection.query("")
87
86
  end
88
- assert_raises ArgumentError do
87
+ assert_raises Vertica::Error::QueryError do
89
88
  @connection.query(nil)
90
89
  end
91
- end
92
-
93
- # FIXME: now hangs forever
94
- # def test_non_query
95
- # @connection.query("--just a comment")
96
- # end
97
-
98
- def test_sql_error
99
90
  assert_raises Vertica::Error::QueryError do
100
- @connection.query("SELECT * FROM nonexisting")
101
- end
102
- assert_raises Vertica::Error::QueryError do
103
- @connection.query("BLAH")
91
+ @connection.query("-- just a SQL comment")
104
92
  end
105
93
  end
106
94
 
107
- def test_cancel
108
- Vertica::Connection.cancel(@connection)
109
- end
110
-
111
- # def test_prepared_statement_with_no_params
112
- # @connection.prepare("my_ps", "SELECT * FROM test_table")
113
- # r = @connection.execute_prepared("my_ps")
114
- # assert_equal 1, r.row_count
115
- # assert_equal 2, r.columns.length
116
- # assert_equal :integer, r.columns[0].data_type
117
- # assert_equal :id, r.columns[0].name
118
- # assert_equal :varchar, r.columns[1].data_type
119
- # assert_equal :name, r.columns[1].name
120
- # assert_equal [{:id => 1, :name => "matt"}], r.rows
121
- # end
122
- #
123
- # def test_prepared_statement_with_one_param
124
- # c = Vertica::Connection.new(TEST_CONNECTION_HASH)
125
- # c.prepare("my_ps", "SELECT * FROM test_table WHERE id = ?", 1)
126
- # r = c.execute_prepared("my_ps", 1)
127
- # assert_equal 1, r.row_count
128
- # assert_equal 2, r.columns.length
129
- # assert_equal :integer, r.columns[0].data_type
130
- # assert_equal 'id', r.columns[0].name
131
- # assert_equal :varchar, r.columns[1].data_type
132
- # assert_equal 'name', r.columns[1].name
133
- # assert_equal [[1, 'matt']], r.rows
134
- # c.close
135
- # end
136
- #
137
- # def test_prepared_statement_with_two_params
138
- # c = Vertica::Connection.new(TEST_CONNECTION_HASH)
139
- # c.prepare("my_ps", "SELECT * FROM test_table WHERE id = ? OR id = ?", 2)
140
- # r = c.execute_prepared("my_ps", 1, 3)
141
- # assert_equal 1, r.row_count
142
- # assert_equal 2, r.columns.length
143
- # assert_equal :integer, r.columns[0].data_type
144
- # assert_equal 'id', r.columns[0].name
145
- # assert_equal :varchar, r.columns[1].data_type
146
- # assert_equal 'name', r.columns[1].name
147
- # assert_equal [[1, 'matt']], r.rows
148
- # c.close
149
- # end
150
-
151
95
  def test_cleanup_after_select
152
- 5.times do
153
- r = @connection.query("SELECT * FROM test_table")
96
+ 3.times do
97
+ r = @connection.query("SELECT * FROM test_ruby_vertica_table")
154
98
  assert_equal 1, r.row_count
155
99
  assert_equal 2, r.columns.length
156
100
  assert_equal :integer, r.columns[0].data_type
@@ -159,5 +103,53 @@ class QueryTest < Test::Unit::TestCase
159
103
  assert_equal :name, r.columns[1].name
160
104
  assert_equal [{:id => 1, :name => "matt"}], r.rows
161
105
  end
106
+ end
107
+
108
+ def test_sql_error
109
+ assert_raises Vertica::Error::QueryError do
110
+ @connection.query("SELECT * FROM nonexistingfdg")
111
+ end
112
+ assert_raises Vertica::Error::QueryError do
113
+ @connection.query("BLAH")
114
+ end
115
+ end
116
+
117
+ def test_copy_in_with_customer_handler
118
+ @connection.copy "COPY test_ruby_vertica_table FROM STDIN" do |data|
119
+ data.copy_data "11|Stuff\r\n"
120
+ data << "12|More stuff\n13|Fin" << "al stuff\n"
121
+ end
122
+
123
+ result = @connection.query("SELECT * FROM test_ruby_vertica_table ORDER BY id", :row_style => :array)
124
+ assert_equal 4, result.length
125
+ assert_equal [[1, "matt"], [11, "Stuff"], [12, "More stuff"], [13, "Final stuff"]], result.rows
126
+ end
127
+
128
+ def test_copy_in_with_file
129
+ filename = File.expand_path('../../resources/test_ruby_vertica_table.csv', __FILE__)
130
+ @connection.copy "COPY test_ruby_vertica_table FROM STDIN", filename
131
+ result = @connection.query("SELECT * FROM test_ruby_vertica_table ORDER BY id", :row_style => :array)
132
+ assert_equal 4, result.length
133
+ assert_equal [[1, "matt"], [11, "Stuff"], [12, "More stuff"], [13, "Final stuff"]], result.rows
134
+ end
135
+
136
+ def test_copy_in_with_io
137
+ io = StringIO.new("11|Stuff\r\n12|More stuff\n13|Final stuff\n")
138
+ @connection.copy "COPY test_ruby_vertica_table FROM STDIN", io
139
+ result = @connection.query("SELECT * FROM test_ruby_vertica_table ORDER BY id", :row_style => :array)
140
+ assert_equal 4, result.length
141
+ assert_equal [[1, "matt"], [11, "Stuff"], [12, "More stuff"], [13, "Final stuff"]], result.rows
142
+ end
143
+
144
+ def test_cancel
145
+ Vertica::Connection.cancel(@connection)
146
+ # TODO: actually test whether this works.
147
+ end
148
+
149
+ def test_notice_handler
150
+ notice_received = false
151
+ @connection.on_notice { |notice| notice_received = true }
152
+ @connection.query('COMMIT')
153
+ assert notice_received
162
154
  end
163
155
  end