vertica 0.9.0.beta7 → 0.9.0.beta8

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