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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8a40a34bb03e1ca8ad79adeb83e8573146675288
4
- data.tar.gz: e80aa43026917a4e59da7f794c4d45d23153c297
3
+ metadata.gz: d40466055bc43ffb3c72810432f2cf6738295399
4
+ data.tar.gz: c4c4c43a8cf12c22ce7cc3b46eadc68d90931453
5
5
  SHA512:
6
- metadata.gz: e1430c2ed8107e26c1a0724d2f0c0ea9e3d21122958e2b9d2b5550a478e83f3a207dc6620a8aa05eb3e76793651237e683769b8ec2c3c4896a8460762b6508be
7
- data.tar.gz: fe886807f5aa39f036dd7d975889fd989283f92068b7a357fa0e479c8af12c016b13a05f6eaa76f104087261ec88b88b2b1e1fa805710aeecf18dbb03f136e39
6
+ metadata.gz: 5d9941765704d6e4c29efa04d1f7dbdf7f79ca787a6c8786019f8659c47f024f7245673cb7185cadce1ea6035bd8bbd2dfa4f526404ab0c922824309ce427c6b
7
+ data.tar.gz: aee68c7643bbafd0399cc3c8ff44b0ea13d9490a1ce553f426579ad87a0e28bf4fb0bc21f73f19bc9ebca85755195a4be485099775336b38a49e69127e4dd3a6
data/README.md CHANGED
@@ -36,31 +36,32 @@ Or add it to your Gemfile:
36
36
  The <code>Vertica.connect</code> methods takes a connection parameter hash and returns a
37
37
  connection object. For most options, the gem will use a default value if no value is provided.
38
38
 
39
- connection = Vertica.connect({
39
+ connection = Vertica.connect(
40
40
  :host => 'db_server',
41
- :user => 'user',
41
+ :username => 'user',
42
42
  :password => 'password',
43
43
  # :ssl => false, # use SSL for the connection
44
44
  # :port => 5433, # default Vertica port: 5433
45
45
  # :database => 'db', # there is only one database
46
46
  # :role => nil, # the (additional) role(s) to enable for the user.
47
47
  # :search_path => nil, # default: <user>,public,v_catalog
48
- # :row_style => :hash # can also be :array (see below)
49
- })
48
+ )
50
49
 
51
50
  To close the connection when you're done with it, run <code>connection.close</code>.
52
51
 
53
52
  You can pass `OpenSSL::SSL::SSLContext` in `:ssl` to customize SSL connection options.
54
53
 
55
- ### Querying with unbuffered result as streaming rows
54
+ ### Running queries
56
55
 
57
- You can run simple queries using the <code>query</code> method, either in buffered and
58
- unbuffered mode. For large result sets, you probably do not want to use buffered results.
56
+ You can run queries using the <code>query</code> method, either in buffered and
57
+ unbuffered mode. For large result sets, you probably do not want to use buffered results,
58
+ because buffering the entire result may require a lot of memory.
59
59
 
60
60
  Get all the result rows without buffering by providing a block:
61
61
 
62
62
  connection.query("SELECT id, name FROM my_table") do |row|
63
- puts row # => {:id => 123, :name => "Jim Bob"}
63
+ puts row['id'] # => 123
64
+ puts row['name'] # => 'Jim Bob'
64
65
  end
65
66
 
66
67
  Note: you can only use the connection for one query at the time. If you try to run another
@@ -73,26 +74,23 @@ Store the result of the query method as a variable to get a buffered resultset:
73
74
  result = connection.query("SELECT id, name FROM my_table")
74
75
  connection.close
75
76
 
76
- result.rows # => [{:id => 123, :name => "Jim Bob"}, {:id => 456, :name => "Joe Jack"}]
77
- result.row_count # => 2
77
+ result.size # => 2
78
78
 
79
79
  result.each do |row|
80
- puts row # => {:id => 123, :name => "Jim Bob"}
80
+ puts row # => Vertica::Row[123, "Jim Bob"]>
81
81
  end
82
82
 
83
- ### Row format
83
+ Rows are provided as `Vertica::Row` instances. You can access the individial fields by
84
+ referring to their name as String or Symbol, or the index of the field in the result.
84
85
 
85
- By default, rows are returned as hashes, using symbols for the column names. Rows can also
86
- be returned as arrays by providing a row_style:
86
+ result.each do |row|
87
+ puts row # => Vertica::Row[123, "Jim Bob"]>
87
88
 
88
- connection.query("SELECT id, name FROM my_table", :row_style => :array) do |row|
89
- puts row # => [123, "Jim Bob"]
89
+ puts row['id'], row[:id], row[] # Three times '123'
90
+ puts row['name'], row[:name], row[1] # Three times 'Jim Bob'
90
91
  end
91
92
 
92
- By adding <code>:row_style => :array</code> to the connection hash, all results will be
93
- returned as array.
94
-
95
- ### Loading data into Vertica using COPY
93
+ ### Loading data into Vertica using COPY statements
96
94
 
97
95
  Using the COPY statement, you can load arbitrary data from your ruby script to the database.
98
96
 
@@ -1,22 +1,23 @@
1
1
  require 'date'
2
+ require 'time'
2
3
  require 'bigdecimal'
3
4
 
4
5
  # Main module for this library. It contains the {.connect} method to return a
5
- # {Vertica::Connection} instance, and methods to quote values ({.quote}) and
6
+ # {Vertica::Connection} instance, and methods to quote values ({.quote}) and
6
7
  # identifiers ({.quote_identifier}) to safely include those in SQL strings to
7
8
  # prevent SQL injection.
8
9
  module Vertica
9
-
10
+
10
11
  # The protocol version (3.0.0) implemented in this library.
11
12
  PROTOCOL_VERSION = 3 << 16
12
13
 
13
14
  # Opens a new connection to a Vertica database.
14
15
  # @param (see Vertica::Connection#initialize)
15
16
  # @return [Vertica::Connection] The created connection to Vertica, ready for queries.
16
- def self.connect(options)
17
- Vertica::Connection.new(options)
17
+ def self.connect(**kwargs)
18
+ Vertica::Connection.new(**kwargs)
18
19
  end
19
-
20
+
20
21
  # Properly quotes a value for safe usage in SQL queries.
21
22
  #
22
23
  # This method has quoting rules for common types. Any other object will be converted to
@@ -29,8 +30,8 @@ module Vertica
29
30
  when nil then 'NULL'
30
31
  when false then 'FALSE'
31
32
  when true then 'TRUE'
32
- when DateTime then value.strftime("'%Y-%m-%d %H:%M:%S'::timestamp")
33
- when Time then value.strftime("'%Y-%m-%d %H:%M:%S'::timestamp")
33
+ when DateTime then value.strftime("'%Y-%m-%dT%H:%M:%S.%6N%z'::timestamptz")
34
+ when Time then value.strftime("'%Y-%m-%dT%H:%M:%S.%6N%z'::timestamptz")
34
35
  when Date then value.strftime("'%Y-%m-%d'::date")
35
36
  when String then "'#{value.gsub(/'/, "''")}'"
36
37
  when BigDecimal then value.to_s('F')
@@ -39,7 +40,7 @@ module Vertica
39
40
  else self.quote(value.to_s)
40
41
  end
41
42
  end
42
-
43
+
43
44
  # Quotes an identifier for safe use within SQL queries, using double quotes.
44
45
  # @param [:to_s] identifier The identifier to quote.
45
46
  # @return [String] The quoted identifier that can be safely included in SQL queries.
@@ -51,3 +52,9 @@ end
51
52
  require 'vertica/version'
52
53
  require 'vertica/error'
53
54
  require 'vertica/connection'
55
+ require 'vertica/query'
56
+ require 'vertica/column'
57
+ require 'vertica/row_description'
58
+ require 'vertica/row'
59
+ require 'vertica/result'
60
+ require 'vertica/protocol/message'
@@ -2,9 +2,11 @@ module Vertica
2
2
  class Column
3
3
  attr_reader :name
4
4
  attr_reader :table_oid
5
- attr_reader :type_modifier
6
- attr_reader :size
5
+ attr_reader :attribute_number
6
+ attr_reader :format
7
7
  attr_reader :data_type
8
+ attr_reader :data_type_size
9
+ attr_reader :data_type_modifier
8
10
 
9
11
  STRING_CONVERTER = lambda { |s| s.force_encoding('utf-8') }
10
12
 
@@ -32,10 +34,10 @@ module Vertica
32
34
  7 => [:float, FLOAT_CONVERTER],
33
35
  8 => [:char, STRING_CONVERTER],
34
36
  9 => [:varchar, STRING_CONVERTER],
35
- 10 => [:date, lambda { |s| Date.new(*s.split("-").map{|x| x.to_i}) }],
37
+ 10 => [:date, lambda { |s| Date.parse(s) }],
36
38
  11 => [:time, nil],
37
- 12 => [:timestamp, lambda { |s| DateTime.parse(s, true) }],
38
- 13 => [:timestamp_tz, lambda { |s| DateTime.parse(s, true) }],
39
+ 12 => [:timestamp, lambda { |s| Time.parse(s) }],
40
+ 13 => [:timestamp_tz, lambda { |s| Time.parse(s) }],
39
41
  14 => [:interval, nil],
40
42
  15 => [:time_tz, nil],
41
43
  16 => [:numeric, lambda { |s| BigDecimal.new(s) }],
@@ -46,15 +48,32 @@ module Vertica
46
48
 
47
49
  DATA_TYPES = DATA_TYPE_CONVERSIONS.values.map { |t| t[0] }
48
50
 
49
- def initialize(col)
50
- @type_modifier = col.fetch(:type_modifier)
51
- @format = col.fetch(:format_code) == 0 ? :text : :binary
52
- @table_oid = col.fetch(:table_oid)
53
- @name = col.fetch(:name).to_sym
54
- @attribute_number = col.fetch(:attribute_number)
55
- @size = col.fetch(:data_type_size)
51
+ def initialize(name: nil, table_oid: nil, attribute_number: nil, format_code: 0, data_type_oid: nil, data_type_size: nil, data_type_modifier: nil)
52
+ @name = name
53
+ @table_oid = table_oid
54
+ @attribute_number = attribute_number
56
55
 
57
- @data_type, @converter = column_type_from_oid(col.fetch(:data_type_oid))
56
+ @format = format_code == 0 ? :text : :binary
57
+ @data_type_size = data_type_size
58
+ @data_type_modifier = data_type_modifier
59
+ @data_type, @converter = column_type_from_oid(data_type_oid)
60
+ end
61
+
62
+ def eql?(other)
63
+ self.class === other &&
64
+ other.name == name &&
65
+ other.format == format &&
66
+ other.data_type == data_type &&
67
+ other.data_type_size == data_type_size &&
68
+ other.data_type_modifier == data_type_modifier &&
69
+ other.table_oid == table_oid &&
70
+ other.attribute_number == attribute_number
71
+ end
72
+
73
+ alias_method :==, :eql?
74
+
75
+ def hash
76
+ [name, format, data_type, data_type_size, data_type_modifier, table_oid, attribute_number].hash
58
77
  end
59
78
 
60
79
  def convert(s)
@@ -2,56 +2,37 @@ require 'socket'
2
2
 
3
3
  class Vertica::Connection
4
4
 
5
- attr_reader :notices, :transaction_status, :backend_pid, :backend_key, :parameters, :notice_handler, :session_id
6
-
7
- attr_accessor :row_style, :debug, :options
8
-
9
- def self.cancel(existing_conn)
10
- existing_conn.cancel
11
- end
5
+ attr_reader :transaction_status, :parameters, :options
12
6
 
13
7
  # Opens a connectio the a Vertica server
14
8
  # @param [Hash] options The connection options to use.
15
- def initialize(options = {})
16
- reset_values
9
+ def initialize(host: nil, port: 5433, username: nil, password: nil, database: nil, interruptable: false, ssl: nil, read_timeout: 600, debug: false, role: nil, search_path: nil, timezone: nil, autocommit: false, skip_startup: false, skip_initialize: false, user: nil)
10
+ reset_state
17
11
  @notice_handler = nil
18
12
 
19
- @options = {}
20
- @debug = false
21
-
22
- options.each { |key, value| @options[key.to_s.to_sym] = value if value}
23
-
24
- @options[:port] ||= 5433
25
- @options[:read_timeout] ||= 600
26
-
27
- @row_style = @options[:row_style] ? @options[:row_style] : :hash
28
- boot_connection unless options[:skip_startup]
13
+ @options = {
14
+ host: host,
15
+ port: port.to_i,
16
+ username: username || user,
17
+ password: password,
18
+ database: database,
19
+ debug: debug,
20
+ ssl: ssl,
21
+ interruptable: interruptable,
22
+ read_timeout: read_timeout,
23
+ role: role,
24
+ search_path: search_path,
25
+ timezone: timezone,
26
+ autocommit: autocommit,
27
+ }
28
+
29
+ boot_connection(skip_initialize: skip_initialize) unless skip_startup
29
30
  end
30
31
 
31
32
  def on_notice(&block)
32
33
  @notice_handler = block
33
34
  end
34
35
 
35
- def socket
36
- @socket ||= begin
37
- raw_socket = TCPSocket.new(@options[:host], @options[:port].to_i)
38
- if @options[:ssl]
39
- require 'openssl'
40
- raw_socket.write Vertica::Messages::SslRequest.new.to_bytes
41
- if raw_socket.read(1) == 'S'
42
- ssl_context = @options[:ssl].is_a?(OpenSSL::SSL::SSLContext) ? @options[:ssl] : OpenSSL::SSL::SSLContext.new
43
- raw_socket = OpenSSL::SSL::SSLSocket.new(raw_socket, ssl_context)
44
- raw_socket.sync = true
45
- raw_socket.connect
46
- else
47
- raise Vertica::Error::SSLNotSupported.new("SSL requested but server doesn't support it.")
48
- end
49
- end
50
-
51
- raw_socket
52
- end
53
- end
54
-
55
36
  def ssl?
56
37
  Object.const_defined?('OpenSSL') && @socket.kind_of?(OpenSSL::SSL::SSLSocket)
57
38
  end
@@ -72,81 +53,90 @@ class Vertica::Connection
72
53
  !busy?
73
54
  end
74
55
 
75
- def write_message(message)
76
- puts "=> #{message.inspect}" if @debug
77
- write_bytes message.to_bytes
78
- rescue SystemCallError, IOError => e
79
- close_socket
80
- raise Vertica::Error::ConnectionError.new(e.message)
56
+ def interruptable?
57
+ !session_id.nil?
81
58
  end
82
59
 
83
- def close
84
- write_message Vertica::Messages::Terminate.new
85
- ensure
86
- close_socket
60
+ def query(sql, **kwargs, &block)
61
+ row_handler = block_given? ? block : nil
62
+ job = Vertica::Query.new(self, sql, row_handler: row_handler, **kwargs)
63
+ run_with_mutex(job)
87
64
  end
88
65
 
89
- def close_socket
90
- socket.close
91
- @socket = nil
92
- rescue SystemCallError, IOError
93
- ensure
94
- reset_values
66
+ def copy(sql, source: nil, **kwargs, &block)
67
+ copy_handler = if block_given?
68
+ block
69
+ elsif source && File.exist?(source.to_s)
70
+ lambda { |data| file_copy_handler(source, data) }
71
+ elsif source.respond_to?(:read) && source.respond_to?(:eof?)
72
+ lambda { |data| io_copy_handler(source, data) }
73
+ end
74
+
75
+ job = Vertica::Query.new(self, sql, copy_handler: copy_handler, **kwargs)
76
+
77
+ run_with_mutex(job)
95
78
  end
96
79
 
97
- def reset_connection
98
- close
99
- boot_connection
80
+ def inspect
81
+ safe_options = @options.reject { |name, _| name == :password }
82
+ "#<Vertica::Connection:#{object_id} @parameters=#{@parameters.inspect} @backend_pid=#{@backend_pid}, @backend_key=#{@backend_key}, @transaction_status=#{@transaction_status}, @socket=#{@socket}, @options=#{safe_options.inspect}>"
100
83
  end
101
84
 
102
- def boot_connection
103
- startup_connection
104
- initialize_connection
85
+ def close
86
+ write_message(Vertica::Protocol::Terminate.new)
87
+ ensure
88
+ close_socket
105
89
  end
106
90
 
107
91
  def cancel
108
- conn = self.class.new(options.merge(:skip_startup => true))
109
- conn.write_message Vertica::Messages::CancelRequest.new(backend_pid, backend_key)
110
- conn.write_message Vertica::Messages::Flush.new
111
- conn.socket.close
92
+ conn = self.class.new(skip_startup: true, **options)
93
+ conn.write_message(Vertica::Protocol::CancelRequest.new(backend_pid, backend_key))
94
+ conn.write_message(Vertica::Protocol::Flush.new)
95
+ conn.close_socket
112
96
  end
113
97
 
114
98
  def interrupt
115
99
  raise Vertica::Error::InterruptImpossible, "Session cannopt be interrupted because the session ID is not known!" if session_id.nil?
116
- conn = self.class.new(options.merge(:interruptable => false, :role => nil, :search_path => nil))
117
- response = conn.query("SELECT CLOSE_SESSION(#{Vertica.quote(session_id)})").the_value
118
- conn.close
119
- return response
100
+ conn = self.class.new(skip_initialize: true, **options)
101
+ conn.query("SELECT CLOSE_SESSION(#{Vertica.quote(session_id)})").the_value
102
+ ensure
103
+ conn.close if conn
120
104
  end
121
105
 
122
- def interruptable?
123
- !session_id.nil?
106
+ # @private
107
+ def write_message(message)
108
+ puts "=> #{message.inspect}" if options.fetch(:debug)
109
+ write_bytes(message.to_bytes)
110
+ rescue SystemCallError, IOError => e
111
+ close_socket
112
+ raise Vertica::Error::ConnectionError.new(e.message)
124
113
  end
125
114
 
115
+ # @private
126
116
  def read_message
127
- type = read_bytes(1)
128
- size = read_bytes(4).unpack('N').first
117
+ type, size = read_bytes(5).unpack('aN')
129
118
  raise Vertica::Error::MessageError.new("Bad message size: #{size}.") unless size >= 4
130
- message = Vertica::Messages::BackendMessage.factory type, read_bytes(size - 4)
131
- puts "<= #{message.inspect}" if @debug
119
+ message = Vertica::Protocol::BackendMessage.factory(type, read_bytes(size - 4))
120
+ puts "<= #{message.inspect}" if options.fetch(:debug)
132
121
  return message
133
122
  rescue SystemCallError, IOError => e
134
123
  close_socket
135
124
  raise Vertica::Error::ConnectionError.new(e.message)
136
125
  end
137
126
 
127
+ # @private
138
128
  def process_message(message)
139
129
  case message
140
- when Vertica::Messages::ErrorResponse
130
+ when Vertica::Protocol::ErrorResponse
141
131
  raise Vertica::Error::ConnectionError.new(message.error_message)
142
- when Vertica::Messages::NoticeResponse
132
+ when Vertica::Protocol::NoticeResponse
143
133
  @notice_handler.call(message) if @notice_handler
144
- when Vertica::Messages::BackendKeyData
134
+ when Vertica::Protocol::BackendKeyData
145
135
  @backend_pid = message.pid
146
136
  @backend_key = message.key
147
- when Vertica::Messages::ParameterStatus
137
+ when Vertica::Protocol::ParameterStatus
148
138
  @parameters[message.name] = message.value
149
- when Vertica::Messages::ReadyForQuery
139
+ when Vertica::Protocol::ReadyForQuery
150
140
  @transaction_status = message.transaction_status
151
141
  @mutex.unlock
152
142
  else
@@ -154,30 +144,29 @@ class Vertica::Connection
154
144
  end
155
145
  end
156
146
 
157
- def query(sql, options = {}, &block)
158
- job = Vertica::Query.new(self, sql, { :row_style => @row_style }.merge(options))
159
- job.row_handler = block if block_given?
160
- run_with_mutex(job)
161
- end
147
+ protected
162
148
 
163
- def copy(sql, source = nil, &block)
164
- job = Vertica::Query.new(self, sql, :row_style => @row_style)
165
- if block_given?
166
- job.copy_handler = block
167
- elsif source && File.exist?(source.to_s)
168
- job.copy_handler = lambda { |data| file_copy_handler(source, data) }
169
- elsif source.respond_to?(:read) && source.respond_to?(:eof?)
170
- job.copy_handler = lambda { |data| io_copy_handler(source, data) }
171
- end
172
- run_with_mutex(job)
173
- end
149
+ attr_reader :backend_pid, :backend_key, :session_id
174
150
 
175
- def inspect
176
- safe_options = @options.reject{ |name, _| name == :password }
177
- "#<Vertica::Connection:#{object_id} @parameters=#{@parameters.inspect} @backend_pid=#{@backend_pid}, @backend_key=#{@backend_key}, @transaction_status=#{@transaction_status}, @socket=#{@socket}, @options=#{safe_options.inspect}, @row_style=#{@row_style}>"
178
- end
151
+ def socket
152
+ @socket ||= begin
153
+ raw_socket = TCPSocket.new(@options[:host], @options[:port].to_i)
154
+ if @options[:ssl]
155
+ require 'openssl'
156
+ raw_socket.write(Vertica::Protocol::SslRequest.new.to_bytes)
157
+ if raw_socket.read(1) == 'S'
158
+ ssl_context = @options[:ssl].is_a?(OpenSSL::SSL::SSLContext) ? @options[:ssl] : OpenSSL::SSL::SSLContext.new
159
+ raw_socket = OpenSSL::SSL::SSLSocket.new(raw_socket, ssl_context)
160
+ raw_socket.sync = true
161
+ raw_socket.connect
162
+ else
163
+ raise Vertica::Error::SSLNotSupported.new("SSL requested but server doesn't support it.")
164
+ end
165
+ end
179
166
 
180
- protected
167
+ raw_socket
168
+ end
169
+ end
181
170
 
182
171
  def run_with_mutex(job)
183
172
  boot_connection if closed?
@@ -193,19 +182,18 @@ class Vertica::Connection
193
182
  end
194
183
  end
195
184
 
196
- COPY_FROM_IO_BLOCK_SIZE = 1024 * 4096
185
+ DEFAULT_IO_COPY_HANDLER_BLOCK_SIZE = 1024 * 4096
186
+ private_constant :DEFAULT_IO_COPY_HANDLER_BLOCK_SIZE
197
187
 
198
188
  def file_copy_handler(input_file, output)
199
189
  File.open(input_file, 'r') do |input|
200
- while data = input.read(COPY_FROM_IO_BLOCK_SIZE)
201
- output << data
202
- end
190
+ io_copy_handler(input, output)
203
191
  end
204
192
  end
205
193
 
206
194
  def io_copy_handler(input, output)
207
195
  until input.eof?
208
- output << input.read(COPY_FROM_IO_BLOCK_SIZE)
196
+ output << input.read(DEFAULT_IO_COPY_HANDLER_BLOCK_SIZE)
209
197
  end
210
198
  end
211
199
 
@@ -241,27 +229,68 @@ class Vertica::Connection
241
229
  end
242
230
 
243
231
  def startup_connection
244
- write_message Vertica::Messages::Startup.new(@options[:user] || @options[:username], @options[:database])
232
+ write_message(Vertica::Protocol::Startup.new(@options[:username], @options[:database]))
245
233
  message = nil
246
234
  begin
247
235
  case message = read_message
248
- when Vertica::Messages::Authentication
249
- if message.code != Vertica::Messages::Authentication::OK
250
- write_message Vertica::Messages::Password.new(@options[:password], message.code, {:user => @options[:user], :salt => message.salt})
236
+ when Vertica::Protocol::Authentication
237
+ if message.code != Vertica::Protocol::Authentication::OK
238
+ write_message(Vertica::Protocol::Password.new(@options[:password], auth_method: message.code, user: @options[:username], salt: message.salt))
251
239
  end
252
240
  else
253
241
  process_message(message)
254
242
  end
255
- end until message.kind_of?(Vertica::Messages::ReadyForQuery)
243
+ end until message.kind_of?(Vertica::Protocol::ReadyForQuery)
256
244
  end
257
245
 
258
246
  def initialize_connection
259
- query("SET SEARCH_PATH TO #{options[:search_path]}") if options[:search_path]
260
- query("SET ROLE #{options[:role]}") if options[:role]
261
247
  @session_id = query("SELECT session_id FROM v_monitor.current_session").the_value if options[:interruptable]
248
+ initialize_connection_with_role
249
+ initialize_connection_with_search_path
250
+ initialize_connection_with_timezone
251
+ initialize_connection_with_autocommit
252
+ end
253
+
254
+ def initialize_connection_with_role
255
+ case options[:role]
256
+ when :all, :none, :default
257
+ query("SET ROLE #{options[:role].to_s.upcase}")
258
+ when String, Array
259
+ query("SET ROLE #{Vertica.quote(options[:role])}")
260
+ end
262
261
  end
263
262
 
264
- def reset_values
263
+ def initialize_connection_with_search_path
264
+ query("SET SEARCH_PATH TO #{Vertica.quote(options[:search_path])}") if options[:search_path]
265
+ end
266
+
267
+ def initialize_connection_with_timezone
268
+ query("SET TIME ZONE TO #{Vertica.quote(options[:timezone])}") if options[:timezone]
269
+ end
270
+
271
+ def initialize_connection_with_autocommit
272
+ query("SET AUTOCOMMIT TO ON") if options[:autocommit]
273
+ end
274
+
275
+ def close_socket
276
+ @socket.close if @socket
277
+ rescue SystemCallError, IOError
278
+ # ignore
279
+ ensure
280
+ reset_state
281
+ end
282
+
283
+ def reset_connection
284
+ close
285
+ boot_connection
286
+ end
287
+
288
+ def boot_connection(skip_initialize: false)
289
+ startup_connection
290
+ initialize_connection unless skip_initialize
291
+ end
292
+
293
+ def reset_state
265
294
  @parameters = {}
266
295
  @session_id = nil
267
296
  @backend_pid = nil
@@ -271,8 +300,3 @@ class Vertica::Connection
271
300
  @mutex = Mutex.new.lock
272
301
  end
273
302
  end
274
-
275
- require 'vertica/query'
276
- require 'vertica/column'
277
- require 'vertica/result'
278
- require 'vertica/messages/message'