vertica 0.12.0 → 1.0.0.rc1

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