vertica 0.9.0.beta7 → 0.9.0.beta8

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.0.beta7
1
+ 0.9.0.beta8
@@ -2,15 +2,12 @@ require 'socket'
2
2
 
3
3
  class Vertica::Connection
4
4
 
5
- attr_reader :options, :notices, :transaction_status, :backend_pid, :backend_key, :parameters, :notice_handler
5
+ attr_reader :options, :notices, :transaction_status, :backend_pid, :backend_key, :parameters, :notice_handler, :session_id
6
6
 
7
7
  attr_accessor :row_style, :debug
8
8
 
9
9
  def self.cancel(existing_conn)
10
- conn = self.new(existing_conn.options.merge(:skip_startup => true))
11
- conn.write Vertica::Messages::CancelRequest.new(existing_conn.backend_pid, existing_conn.backend_key)
12
- conn.write Vertica::Messages::Flush.new
13
- conn.socket.close
10
+ existing_conn.cancel
14
11
  end
15
12
 
16
13
  # Opens a connectio the a Vertica server
@@ -64,6 +61,14 @@ class Vertica::Connection
64
61
  !opened?
65
62
  end
66
63
 
64
+ def busy?
65
+ !ready_for_query?
66
+ end
67
+
68
+ def ready_for_query?
69
+ @ready_for_query == true
70
+ end
71
+
67
72
  def write(message)
68
73
  raise ArgumentError, "invalid message: (#{message.inspect})" unless message.respond_to?(:to_bytes)
69
74
  puts "=> #{message.inspect}" if @debug
@@ -84,6 +89,25 @@ class Vertica::Connection
84
89
  reset_values
85
90
  end
86
91
 
92
+ def cancel
93
+ conn = self.class.new(options.merge(:skip_startup => true))
94
+ conn.write Vertica::Messages::CancelRequest.new(backend_pid, backend_key)
95
+ conn.write Vertica::Messages::Flush.new
96
+ conn.socket.close
97
+ end
98
+
99
+ def interrupt
100
+ raise Vertica::Error::ConnectionError, "Session cannopt be interrupted because the session ID is not known!" if session_id.nil?
101
+ conn = self.class.new(options.merge(:interruptable => false, :role => nil, :search_path => nil))
102
+ response = conn.query("SELECT CLOSE_SESSION(#{Vertica.quote(session_id)})").the_value
103
+ conn.close
104
+ return response
105
+ end
106
+
107
+ def interruptable?
108
+ !session_id.nil?
109
+ end
110
+
87
111
  def read_message
88
112
  type = read_bytes(1)
89
113
  size = read_bytes(4).unpack('N').first
@@ -106,28 +130,38 @@ class Vertica::Connection
106
130
  @parameters[message.name] = message.value
107
131
  when Vertica::Messages::ReadyForQuery
108
132
  @transaction_status = message.transaction_status
133
+ @ready_for_query = true
109
134
  else
110
135
  raise Vertica::Error::MessageError, "Unhandled message: #{message.inspect}"
111
136
  end
112
137
  end
113
138
 
139
+ def with_lock(&block)
140
+ raise Vertica::Error::SynchronizeError, "The connection is in use!" if busy?
141
+ @ready_for_query = false
142
+ yield
143
+ end
114
144
 
115
145
  def query(sql, options = {}, &block)
116
- job = Vertica::Query.new(self, sql, { :row_style => @row_style }.merge(options))
117
- job.row_handler = block if block_given?
118
- return job.run
146
+ with_lock do
147
+ job = Vertica::Query.new(self, sql, { :row_style => @row_style }.merge(options))
148
+ job.row_handler = block if block_given?
149
+ job.run
150
+ end
119
151
  end
120
152
 
121
153
  def copy(sql, source = nil, &block)
122
- job = Vertica::Query.new(self, sql, :row_style => @row_style)
123
- if block_given?
124
- job.copy_handler = block
125
- elsif source && File.exists?(source.to_s)
126
- job.copy_handler = lambda { |data| file_copy_handler(source, data) }
127
- elsif source.respond_to?(:read) && source.respond_to?(:eof?)
128
- job.copy_handler = lambda { |data| io_copy_handler(source, data) }
154
+ with_lock do
155
+ job = Vertica::Query.new(self, sql, :row_style => @row_style)
156
+ if block_given?
157
+ job.copy_handler = block
158
+ elsif source && File.exists?(source.to_s)
159
+ job.copy_handler = lambda { |data| file_copy_handler(source, data) }
160
+ elsif source.respond_to?(:read) && source.respond_to?(:eof?)
161
+ job.copy_handler = lambda { |data| io_copy_handler(source, data) }
162
+ end
163
+ job.run
129
164
  end
130
- return job.run
131
165
  end
132
166
 
133
167
  def inspect
@@ -136,10 +170,12 @@ class Vertica::Connection
136
170
  end
137
171
 
138
172
  protected
139
-
173
+
174
+ COPY_FROM_IO_BLOCK_SIZE = 1024 * 4096
175
+
140
176
  def file_copy_handler(input_file, output)
141
177
  File.open(input_file, 'r') do |input|
142
- while data = input.read(4096)
178
+ while data = input.read(COPY_FROM_IO_BLOCK_SIZE)
143
179
  output << data
144
180
  end
145
181
  end
@@ -147,7 +183,7 @@ class Vertica::Connection
147
183
 
148
184
  def io_copy_handler(input, output)
149
185
  until input.eof?
150
- output << input.read(4096)
186
+ output << input.read(COPY_FROM_IO_BLOCK_SIZE)
151
187
  end
152
188
  end
153
189
 
@@ -175,14 +211,17 @@ class Vertica::Connection
175
211
  def initialize_connection
176
212
  query("SET SEARCH_PATH TO #{options[:search_path]}") if options[:search_path]
177
213
  query("SET ROLE #{options[:role]}") if options[:role]
214
+ @session_id = query("SELECT session_id FROM v_monitor.current_session").the_value if options[:interruptable]
178
215
  end
179
216
 
180
217
  def reset_values
181
218
  @parameters = {}
219
+ @session_id = nil
182
220
  @backend_pid = nil
183
221
  @backend_key = nil
184
222
  @transaction_status = nil
185
223
  @socket = nil
224
+ @ready_for_query = false
186
225
  end
187
226
  end
188
227
 
@@ -0,0 +1,31 @@
1
+ # Main class for exceptions relating to Vertica.
2
+ class Vertica::Error < StandardError
3
+
4
+ class ConnectionError < Vertica::Error; end
5
+ class MessageError < Vertica::Error; end
6
+ class SynchronizeError < Vertica::Error; end
7
+ class EmptyQueryError < Vertica::Error; end
8
+
9
+ class QueryError < Vertica::Error
10
+
11
+ attr_reader :error_response
12
+
13
+ def initialize(error_response)
14
+ @error_response = error_response
15
+ super(error_response.error_message)
16
+ end
17
+
18
+ def self.from_error_response(error_response)
19
+ klass = QUERY_ERROR_CLASSES[error_response.sqlstate] || self
20
+ klass.new(error_response)
21
+ end
22
+ end
23
+
24
+ QUERY_ERROR_CLASSES = {
25
+ '55V03' => (LockFailure = Class.new(Vertica::Error::QueryError)),
26
+ '53200' => (OutOfMemory = Class.new(Vertica::Error::QueryError)),
27
+ '42601' => (SyntaxError = Class.new(Vertica::Error::QueryError)),
28
+ '42V01' => (MissingRelation = Class.new(Vertica::Error::QueryError)),
29
+ '42703' => (MissingColumn = Class.new(Vertica::Error::QueryError))
30
+ }
31
+ end
@@ -4,18 +4,18 @@ module Vertica
4
4
  message_id 'N'
5
5
 
6
6
  FIELDS_DEFINITIONS = [
7
- { :type => 'q', :name => "Internal Query" },
8
- { :type => 'S', :name => "Severity" },
9
- { :type => 'M', :name => "Message" },
10
- { :type => 'C', :name => "Sqlstate" },
11
- { :type => 'D', :name => "Detail" },
12
- { :type => 'H', :name => "Hint" },
13
- { :type => 'P', :name => "Position" },
14
- { :type => 'W', :name => "Where" },
15
- { :type => 'p', :name => "Internal Position" },
16
- { :type => 'R', :name => "Routine" },
17
- { :type => 'F', :name => "File" },
18
- { :type => 'L', :name => "Line" }
7
+ { :type => 'q', :name => "Internal Query", :method => :internal_query },
8
+ { :type => 'S', :name => "Severity", :method => :severity },
9
+ { :type => 'M', :name => "Message", :method => :message },
10
+ { :type => 'C', :name => "Sqlstate", :method => :sqlstate },
11
+ { :type => 'D', :name => "Detail", :method => :detail },
12
+ { :type => 'H', :name => "Hint", :method => :hint },
13
+ { :type => 'P', :name => "Position", :method => :position },
14
+ { :type => 'W', :name => "Where", :method => :where },
15
+ { :type => 'p', :name => "Internal Position", :method => :internal_position },
16
+ { :type => 'R', :name => "Routine", :method => :routine },
17
+ { :type => 'F', :name => "File", :method => :file },
18
+ { :type => 'L', :name => "Line", :method => :line }
19
19
  ]
20
20
 
21
21
  FIELDS = Hash[*FIELDS_DEFINITIONS.map { |f| [f[:type], f[:name]] }.flatten]
@@ -37,6 +37,12 @@ module Vertica
37
37
  end
38
38
  ordered_values.compact.join(', ')
39
39
  end
40
+
41
+ FIELDS_DEFINITIONS.each do |field_def|
42
+ define_method(field_def[:method]) do
43
+ @values[field_def[:name]]
44
+ end
45
+ end
40
46
  end
41
47
  end
42
48
  end
data/lib/vertica/query.rb CHANGED
@@ -18,9 +18,9 @@ class Vertica::Query
18
18
  begin
19
19
  case message = @connection.read_message
20
20
  when Vertica::Messages::ErrorResponse
21
- error = message.error_message
21
+ error = Vertica::Error::QueryError.from_error_response(message)
22
22
  when Vertica::Messages::EmptyQueryResponse
23
- error = "The provided query was empty."
23
+ error = Vertica::Error::EmptyQueryError.new("A SQL string was expected, but the given string was blank or only contained SQL comments.")
24
24
  when Vertica::Messages::CopyInResponse
25
25
  handle_copy_from_stdin
26
26
  when Vertica::Messages::RowDescription, Vertica::Messages::CommandComplete
@@ -30,7 +30,7 @@ class Vertica::Query
30
30
  end
31
31
  end until message.kind_of?(Vertica::Messages::ReadyForQuery)
32
32
 
33
- raise Vertica::Error::QueryError, error unless error.nil?
33
+ raise error unless error.nil?
34
34
  return result
35
35
  end
36
36
 
data/lib/vertica.rb CHANGED
@@ -7,12 +7,6 @@ require 'bigdecimal'
7
7
  # prevent SQL injection.
8
8
  module Vertica
9
9
 
10
- class Error < StandardError
11
- class ConnectionError < Error; end
12
- class MessageError < Error; end
13
- class QueryError < Error; end
14
- end
15
-
16
10
  # The version number of this library.
17
11
  VERSION = File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION])).strip
18
12
 
@@ -57,4 +51,5 @@ module Vertica
57
51
  end
58
52
  end
59
53
 
54
+ require 'vertica/error'
60
55
  require 'vertica/connection'
@@ -62,13 +62,18 @@ class ConnectionTest < Test::Unit::TestCase
62
62
  assert_nil @connection.backend_key
63
63
  assert_nil @connection.transaction_status
64
64
  end
65
+
66
+ def test_interrupt_connection
67
+ @connection = Vertica::Connection.new(TEST_CONNECTION_HASH.merge(:interruptable => true))
68
+ assert @connection.interruptable?
69
+ end
65
70
 
66
71
  def test_new_with_error_response
67
72
  assert_raises Vertica::Error::ConnectionError do
68
73
  Vertica::Connection.new(TEST_CONNECTION_HASH.merge('database' => 'nonexistant_db'))
69
74
  end
70
75
  end
71
-
76
+
72
77
  def test_connection_inspect_should_not_print_password
73
78
  @connection = Vertica::Connection.new(TEST_CONNECTION_HASH)
74
79
  inspected_string = @connection.inspect
@@ -4,15 +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_ruby_vertica_table (id int, name varchar(100))")
7
+ @connection.query("DROP TABLE IF EXISTS test_ruby_vertica_table CASCADE;")
8
+ @connection.query("CREATE TABLE test_ruby_vertica_table (id int, name varchar(100))")
8
9
  @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
10
  @connection.query("INSERT INTO test_ruby_vertica_table VALUES (1, 'matt')")
10
11
  @connection.query("COMMIT")
11
12
  end
12
13
 
13
14
  def teardown
14
- @connection.query("DROP TABLE IF EXISTS test_ruby_vertica_table CASCADE;")
15
- @connection.close
15
+ if @connection.ready_for_query?
16
+ @connection.close
17
+ elsif @connection.interruptable?
18
+ @connection.interrupt
19
+ end
16
20
  end
17
21
 
18
22
  def test_select_query_with_results_as_hash
@@ -81,13 +85,13 @@ class QueryTest < Test::Unit::TestCase
81
85
  end
82
86
 
83
87
  def test_empty_query
84
- assert_raises Vertica::Error::QueryError do
88
+ assert_raises Vertica::Error::EmptyQueryError do
85
89
  @connection.query("")
86
90
  end
87
- assert_raises Vertica::Error::QueryError do
91
+ assert_raises Vertica::Error::EmptyQueryError do
88
92
  @connection.query(nil)
89
93
  end
90
- assert_raises Vertica::Error::QueryError do
94
+ assert_raises Vertica::Error::EmptyQueryError do
91
95
  @connection.query("-- just a SQL comment")
92
96
  end
93
97
  end
@@ -106,10 +110,13 @@ class QueryTest < Test::Unit::TestCase
106
110
  end
107
111
 
108
112
  def test_sql_error
109
- assert_raises Vertica::Error::QueryError do
110
- @connection.query("SELECT * FROM nonexistingfdg")
113
+ assert_raises Vertica::Error::MissingColumn do
114
+ @connection.query("SELECT asad FROM test_ruby_vertica_table")
115
+ end
116
+ assert_raises Vertica::Error::MissingRelation do
117
+ @connection.query("SELECT * FROM nonexisting_dsfdfsdfsdfs")
111
118
  end
112
- assert_raises Vertica::Error::QueryError do
119
+ assert_raises Vertica::Error::SyntaxError do
113
120
  @connection.query("BLAH")
114
121
  end
115
122
  end
@@ -153,15 +160,65 @@ class QueryTest < Test::Unit::TestCase
153
160
  assert_equal [[1, "matt"], [11, "Stuff"], [12, "More stuff"], [13, "Final stuff"]], result.rows
154
161
  end
155
162
 
156
- def test_cancel
157
- Vertica::Connection.cancel(@connection)
158
- # TODO: actually test whether this works.
159
- end
160
-
161
163
  def test_notice_handler
162
164
  notice_received = false
163
165
  @connection.on_notice { |notice| notice_received = true }
164
166
  @connection.query('COMMIT')
165
167
  assert notice_received
166
168
  end
169
+
170
+ def test_query_mutex
171
+ mutex = Mutex.new
172
+ values = []
173
+ t1 = Thread.new do
174
+ mutex.synchronize do
175
+ 3.times { values << @connection.query("SELECT 1").the_value }
176
+ end
177
+ end
178
+ t2 = Thread.new do
179
+ mutex.synchronize do
180
+ 3.times { values << @connection.query("SELECT 2").the_value }
181
+ end
182
+ end
183
+ t3 = Thread.new do
184
+ mutex.synchronize do
185
+ 3.times { values << @connection.query("SELECT 3").the_value }
186
+ end
187
+ end
188
+
189
+ t1.join
190
+ t2.join
191
+ t3.join
192
+
193
+ assert_equal values.sort, [1,1,1,2,2,2,3,3,3]
194
+ end
195
+
196
+ def test_raise_on_synchronisity_problems
197
+ assert_raise(Vertica::Error::SynchronizeError) do
198
+ @connection.query("SELECT 1 UNION SELECT 2") do |record|
199
+ @connection.query("SELECT 3")
200
+ end
201
+ end
202
+ end
203
+
204
+ def test_interrupting_connections
205
+ before = @connection.query("SELECT COUNT(1) FROM test_ruby_vertica_table").the_value
206
+ interruptable = Vertica::Connection.new(TEST_CONNECTION_HASH.merge(:interruptable => true))
207
+ assert interruptable.interruptable?
208
+ t = Thread.new do
209
+ Thread.current[:error_occurred] = false
210
+ begin
211
+ 10.times { |n| interruptable.query("INSERT INTO test_ruby_vertica_table VALUES (#{n}, 'interrupt test')") }
212
+ interruptable.query("COMMIT")
213
+ rescue Vertica::Error::ConnectionError
214
+ Thread.current[:error_occurred] = true
215
+ end
216
+ end
217
+
218
+ interruptable.interrupt
219
+ t.join
220
+
221
+ assert t[:error_occurred], "Expected an exception to occur"
222
+ assert_equal before, @connection.query("SELECT COUNT(1) FROM test_ruby_vertica_table").the_value
223
+ end
167
224
  end
@@ -41,7 +41,16 @@ class BackendMessageTest < Test::Unit::TestCase
41
41
  assert_equal "Severity: FATAL, Message: database \"nonexistant_db\" does not exist, Sqlstate: 3D000, Routine: ClientAuthentication, File: /scratch_a/release/vbuild/vertica/Basics/ClientAuthentication.cpp, Line: 1496", msg.error_message
42
42
  end
43
43
 
44
- def test_notice_response_message
44
+ def test_error_response_fields
45
+ data = "SFATAL\x00C3D000\x00Mdatabase \"nonexistant_db\" does not exist\x00F/scratch_a/release/vbuild/vertica/Basics/ClientAuthentication.cpp\x00L1496\x00RClientAuthentication\x00\x00"
46
+ msg = Vertica::Messages::ErrorResponse.new(data)
47
+
48
+ assert_equal "FATAL", msg.severity
49
+ assert_equal "ClientAuthentication", msg.routine
50
+ end
51
+
52
+
53
+ def test_notice_response_values
45
54
  data = "SINFO\x00C00000\x00Mcannot commit; no transaction in progress\x00F/scratch_a/release/vbuild/vertica/Commands/PGCall.cpp\x00L3502\x00Rprocess_vertica_transaction\x00\x00"
46
55
  msg = Vertica::Messages::NoticeResponse.new(data)
47
56
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vertica
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0.beta7
4
+ version: 0.9.0.beta8
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -11,12 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2011-11-15 00:00:00.000000000 -05:00
15
- default_executable:
14
+ date: 2012-02-20 00:00:00.000000000 Z
16
15
  dependencies:
17
16
  - !ruby/object:Gem::Dependency
18
17
  name: rake
19
- requirement: &2153826360 !ruby/object:Gem::Requirement
18
+ requirement: &2157181100 !ruby/object:Gem::Requirement
20
19
  none: false
21
20
  requirements:
22
21
  - - ! '>='
@@ -24,10 +23,10 @@ dependencies:
24
23
  version: '0'
25
24
  type: :runtime
26
25
  prerelease: false
27
- version_requirements: *2153826360
26
+ version_requirements: *2157181100
28
27
  - !ruby/object:Gem::Dependency
29
28
  name: jeweler
30
- requirement: &2153825660 !ruby/object:Gem::Requirement
29
+ requirement: &2157194780 !ruby/object:Gem::Requirement
31
30
  none: false
32
31
  requirements:
33
32
  - - ! '>='
@@ -35,7 +34,7 @@ dependencies:
35
34
  version: '0'
36
35
  type: :runtime
37
36
  prerelease: false
38
- version_requirements: *2153825660
37
+ version_requirements: *2157194780
39
38
  description: Query Vertica with ruby
40
39
  email: sprsquish@gmail.com
41
40
  executables: []
@@ -53,6 +52,7 @@ files:
53
52
  - lib/vertica.rb
54
53
  - lib/vertica/column.rb
55
54
  - lib/vertica/connection.rb
55
+ - lib/vertica/error.rb
56
56
  - lib/vertica/messages/backend_messages/authentication.rb
57
57
  - lib/vertica/messages/backend_messages/backend_key_data.rb
58
58
  - lib/vertica/messages/backend_messages/bind_complete.rb
@@ -97,7 +97,6 @@ files:
97
97
  - test/unit/backend_message_test.rb
98
98
  - test/unit/frontend_message_test.rb
99
99
  - test/unit/quoting_test.rb
100
- has_rdoc: true
101
100
  homepage: http://github.com/sprsquish/vertica
102
101
  licenses: []
103
102
  post_install_message:
@@ -118,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
117
  version: 1.3.1
119
118
  requirements: []
120
119
  rubyforge_project:
121
- rubygems_version: 1.6.2
120
+ rubygems_version: 1.8.10
122
121
  signing_key:
123
122
  specification_version: 3
124
123
  summary: Pure ruby library for interacting with Vertica