vertica 1.0.0.rc1 → 1.0.0.rc2
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.
- checksums.yaml +4 -4
- data/.yardopts +4 -0
- data/README.md +97 -62
- data/lib/vertica.rb +3 -2
- data/lib/vertica/column.rb +50 -84
- data/lib/vertica/connection.rb +173 -18
- data/lib/vertica/data_type.rb +146 -0
- data/lib/vertica/error.rb +1 -0
- data/lib/vertica/protocol/backend/command_complete.rb +2 -11
- data/lib/vertica/protocol/backend/row_description.rb +1 -1
- data/lib/vertica/query.rb +36 -18
- data/lib/vertica/result.rb +69 -5
- data/lib/vertica/row.rb +57 -6
- data/lib/vertica/row_description.rb +56 -3
- data/lib/vertica/version.rb +3 -1
- data/test/functional/functional_connection_test.rb +2 -2
- data/test/functional/functional_query_test.rb +14 -11
- data/test/functional/functional_type_deserialization_test.rb +179 -0
- data/test/unit/backend_message_test.rb +3 -5
- data/test/unit/column_test.rb +21 -31
- data/test/unit/data_type_test.rb +72 -0
- data/test/unit/result_test.rb +2 -2
- data/test/unit/row_description_test.rb +31 -2
- data/test/unit/row_test.rb +26 -2
- metadata +8 -4
- data/test/functional/functional_value_conversion_test.rb +0 -117
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd8271cb188eef45ec983912494a8ebb72209567
|
4
|
+
data.tar.gz: 730d859a30f294766df5e0a789ecb79de550771c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 504edb900db1f059bb19cb9f619f8d5cf51586bbd8184fb0bfe1bad1f9b208cb49704c21f0e5b2c76de502103035e2f90d7d3086b7dcebc699f5e6b911c2172e
|
7
|
+
data.tar.gz: 4560a75f5a0f2926cf95f95ea3514b578c7779a082148e3af86b84e22091f392a788f084d46b0bcdda309c6a0040628db9e79e5ff44b28b162815312901b5325
|
data/.yardopts
ADDED
data/README.md
CHANGED
@@ -6,50 +6,56 @@ about Vertica at http://www.vertica.com.
|
|
6
6
|
- Connecting, including over SSL.
|
7
7
|
- Executing queries, with results as streaming rows or buffered resultsets.
|
8
8
|
- `COPY table FROM STDIN` statement to load data from your application.
|
9
|
-
- Confirmed to work with Ruby 1.9, 2.0, and 2.1; JRuby 1.7.23 and 9.0.4.0; and
|
10
|
-
with Vertica version 6.x, and 7.x.
|
11
9
|
- The library is thread-safe as of version 0.11. However, you can only run one
|
12
|
-
statement at the time per connection, because the protocol is stateful.
|
10
|
+
statement at the time per connection, because the protocol is stateful. In a
|
11
|
+
multi-threaded environment, you may want to tthink about setting up a
|
12
|
+
connection pool.
|
13
13
|
|
14
14
|
|
15
15
|
## Installation
|
16
16
|
|
17
|
-
|
17
|
+
Add it to the Gemfile of your project:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
gem 'vertica'
|
19
|
+
gem 'vertica', '~> 1.0'
|
22
20
|
# gem 'vertica', git: 'git://github.com/wvanbergen/vertica.git' # HEAD version
|
23
21
|
|
24
|
-
|
22
|
+
Now, run `bundle install`.
|
25
23
|
|
26
|
-
|
27
|
-
- Vertica versions 4.x, and 5.x worked with at some point with this gem, but
|
28
|
-
compatibility is no longer tested. It probably still works as the protocol hasn't
|
29
|
-
changed as far as I am aware.
|
24
|
+
### Compatiblity
|
30
25
|
|
26
|
+
- Ruby 2.0 or higher is required.
|
27
|
+
- Compatibility is tested with Vertica 7.x community edition. Vertica versions 4.x, 5.x,
|
28
|
+
and 6.x worked with at some point with this gem, but compatibility is no longer tested.
|
31
29
|
|
32
30
|
## Usage
|
33
31
|
|
34
32
|
### Connecting
|
35
33
|
|
36
|
-
The
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
34
|
+
The `Vertica.connect` methods takes keyword arguments and returns a connection
|
35
|
+
instance. For most options, the gem will use a default value if no value is provided.
|
36
|
+
|
37
|
+
``` ruby
|
38
|
+
connection = Vertica.connect(
|
39
|
+
host: 'db_server',
|
40
|
+
username: 'user',
|
41
|
+
password: 'password',
|
42
|
+
|
43
|
+
# ssl: false, # use SSL for the connection
|
44
|
+
# port: 5433, # default Vertica port: 5433
|
45
|
+
# database: nil, # there is only one database
|
46
|
+
# role: nil, # the (additional) role(s) to enable for the user.
|
47
|
+
# search_path: nil, # default: <user>,public,v_catalog
|
48
|
+
# timezone: nil, # the timezone for the connection to convert timestamps
|
49
|
+
# autocommit: false, # automatically commit INSERT/UPDATE/DELETE queries
|
50
|
+
# interruptable: false, # set to true to allow sessions to be interrupted.
|
51
|
+
# read_timeout: 600, # timeout in seconds when reading data
|
52
|
+
# debug: false, # print all the messages back and forth to STDOUT.
|
53
|
+
)
|
54
|
+
```
|
55
|
+
|
56
|
+
- To close the connection when you're done with it, run `connection.close`.
|
57
|
+
- You can pass `OpenSSL::SSL::SSLContext` in `:ssl` to customize SSL connection options,
|
58
|
+
or `true` to use the default (`OpenSSL::SSL::SSLContext.new`).
|
53
59
|
|
54
60
|
### Running queries
|
55
61
|
|
@@ -59,10 +65,12 @@ because buffering the entire result may require a lot of memory.
|
|
59
65
|
|
60
66
|
Get all the result rows without buffering by providing a block:
|
61
67
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
68
|
+
``` ruby
|
69
|
+
connection.query("SELECT id, name FROM my_table") do |row|
|
70
|
+
puts row['id'] # => 123
|
71
|
+
puts row['name'] # => 'Unicorn'
|
72
|
+
end
|
73
|
+
```
|
66
74
|
|
67
75
|
Note: you can only use the connection for one query at the time. If you try to run another
|
68
76
|
query when the connection is still busy delivering the results of a previous query, a
|
@@ -71,42 +79,70 @@ problem.
|
|
71
79
|
|
72
80
|
Store the result of the query method as a variable to get a buffered resultset:
|
73
81
|
|
74
|
-
|
75
|
-
|
82
|
+
``` ruby
|
83
|
+
result = connection.query("SELECT id, name FROM my_table")
|
84
|
+
connection.close # buffered result will be available even after closing the connection.
|
76
85
|
|
77
|
-
|
86
|
+
result.size # => 2
|
78
87
|
|
79
|
-
|
80
|
-
|
81
|
-
|
88
|
+
result.each do |row|
|
89
|
+
puts "Hello #{row['name']}, your ID is #{row['id']}."
|
90
|
+
end
|
91
|
+
```
|
82
92
|
|
83
93
|
Rows are provided as `Vertica::Row` instances. You can access the individial fields by
|
84
94
|
referring to their name as String or Symbol, or the index of the field in the result.
|
85
95
|
|
86
|
-
|
87
|
-
|
96
|
+
``` ruby
|
97
|
+
result.each do |row|
|
98
|
+
p row # => Vertica::Row[123, "Unicorn"]>
|
88
99
|
|
89
|
-
|
90
|
-
|
91
|
-
|
100
|
+
puts row['id'], row[:id], row[0] # Three times 123
|
101
|
+
puts row['name'], row[:name], row[1] # Three times 'Unicorn'
|
102
|
+
end
|
103
|
+
```
|
92
104
|
|
93
105
|
### Loading data into Vertica using COPY statements
|
94
106
|
|
95
|
-
Using the COPY statement, you can load arbitrary data from your ruby script to the database.
|
107
|
+
Using the `COPY FROM STDIN` statement, you can load arbitrary data from your ruby script to the database.
|
96
108
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
109
|
+
``` ruby
|
110
|
+
connection.copy("COPY table FROM STDIN ...") do |stdin|
|
111
|
+
File.open('data.tsv', 'r') do |f|
|
112
|
+
begin
|
113
|
+
stdin << f.gets
|
114
|
+
end until f.eof?
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
104
118
|
|
105
119
|
You can also provide a filename or an IO object:
|
106
120
|
|
107
|
-
|
108
|
-
|
121
|
+
``` ruby
|
122
|
+
connection.copy("COPY table FROM STDIN ...", source: "data.csv")
|
123
|
+
File.open('file.csv') do |io|
|
124
|
+
connection.copy("COPY table FROM STDIN ...", source: io)
|
125
|
+
end
|
126
|
+
```
|
109
127
|
|
128
|
+
For more information, see [the Vertica documentation](https://my.vertica.com/docs/7.1.x/HTML/Content/Authoring/SQLReferenceManual/Statements/COPY/COPY.htm)
|
129
|
+
|
130
|
+
### Interrupting sessions
|
131
|
+
|
132
|
+
``` ruby
|
133
|
+
connection = Vertica.connect(host: 'dbserver', ...)
|
134
|
+
|
135
|
+
Thread.new do
|
136
|
+
sleep(60)
|
137
|
+
connection.interrupt
|
138
|
+
end
|
139
|
+
|
140
|
+
begin
|
141
|
+
result = connection.query('SELECT complicated_query FROM huge_table')
|
142
|
+
rescue Vertica::Error::ConnectionError
|
143
|
+
# ...
|
144
|
+
end
|
145
|
+
```
|
110
146
|
|
111
147
|
## About
|
112
148
|
|
@@ -114,17 +150,15 @@ This package is MIT licensed. See the LICENSE file for more information.
|
|
114
150
|
|
115
151
|
### Development
|
116
152
|
|
117
|
-
This project comes with a test suite. The unit tests in
|
118
|
-
connection to run, the functional tests in
|
153
|
+
This project comes with a test suite. The unit tests in `/test/unit` do not need a database
|
154
|
+
connection to run, the functional tests in `/test/functional` do need a working
|
119
155
|
database connection. You can specify the connection parameters by copying the file
|
120
|
-
|
156
|
+
`/test/connection.yml.example` to `/test/connection.yml` and filling out the
|
121
157
|
necessary fields.
|
122
158
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
The test suite is also run by Travis CI againast Vertica 7.0.1, and Ruby 1.9.3, 2.0.0, and 2.1.1.
|
159
|
+
The `/vagrant` folder contains a Vagrantfile and a setup script to help you set up a development
|
160
|
+
database that you can run the functional test suite against. The full test suite is also run by
|
161
|
+
Travis CI against Vertica 7 CE, and against several Ruby versions.
|
128
162
|
|
129
163
|
### Authors
|
130
164
|
|
@@ -137,6 +171,7 @@ The test suite is also run by Travis CI againast Vertica 7.0.1, and Ruby 1.9.3,
|
|
137
171
|
|
138
172
|
* [Website](http://vanbergen.org/vertica)
|
139
173
|
* [API Documentation](http://www.rubydoc.info/gems/vertica/frames)
|
174
|
+
* [Vertica documentation](https://my.vertica.com/docs/7.1.x/HTML/index.htm)
|
140
175
|
* [sequel-vertica](https://github.com/camilo/sequel-vertica): Sequel integration
|
141
176
|
* [newrelic-vertica](https://github.com/wvanbergen/newrelic-vertica): NewRelic monitoring of queries
|
142
177
|
* [node-vertica](https://github.com/wvanbergen/node-vertica): node.js Vertica driver
|
data/lib/vertica.rb
CHANGED
@@ -23,7 +23,7 @@ module Vertica
|
|
23
23
|
# This method has quoting rules for common types. Any other object will be converted to
|
24
24
|
# a string using +:to_s+ and then quoted as a string.
|
25
25
|
#
|
26
|
-
# @param [Object]
|
26
|
+
# @param value [Object] The value to quote.
|
27
27
|
# @return [String] The quoted value that can be safely included in SQL queries.
|
28
28
|
def self.quote(value)
|
29
29
|
case value
|
@@ -42,7 +42,7 @@ module Vertica
|
|
42
42
|
end
|
43
43
|
|
44
44
|
# Quotes an identifier for safe use within SQL queries, using double quotes.
|
45
|
-
# @param [:to_s]
|
45
|
+
# @param identifier [:to_s] The identifier to quote.
|
46
46
|
# @return [String] The quoted identifier that can be safely included in SQL queries.
|
47
47
|
def self.quote_identifier(identifier)
|
48
48
|
"\"#{identifier.to_s.gsub(/"/, '""')}\""
|
@@ -53,6 +53,7 @@ require 'vertica/version'
|
|
53
53
|
require 'vertica/error'
|
54
54
|
require 'vertica/connection'
|
55
55
|
require 'vertica/query'
|
56
|
+
require 'vertica/data_type'
|
56
57
|
require 'vertica/column'
|
57
58
|
require 'vertica/row_description'
|
58
59
|
require 'vertica/row'
|
data/lib/vertica/column.rb
CHANGED
@@ -1,92 +1,58 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
2 => [:pos, nil],
|
30
|
-
3 => [:record, nil],
|
31
|
-
4 => [:unknown, nil],
|
32
|
-
5 => [:bool, lambda { |s| s == 't' }],
|
33
|
-
6 => [:integer, lambda { |s| s.to_i }],
|
34
|
-
7 => [:float, FLOAT_CONVERTER],
|
35
|
-
8 => [:char, STRING_CONVERTER],
|
36
|
-
9 => [:varchar, STRING_CONVERTER],
|
37
|
-
10 => [:date, lambda { |s| Date.parse(s) }],
|
38
|
-
11 => [:time, nil],
|
39
|
-
12 => [:timestamp, lambda { |s| Time.parse(s) }],
|
40
|
-
13 => [:timestamp_tz, lambda { |s| Time.parse(s) }],
|
41
|
-
14 => [:interval, nil],
|
42
|
-
15 => [:time_tz, nil],
|
43
|
-
16 => [:numeric, lambda { |s| BigDecimal.new(s) }],
|
44
|
-
17 => [:bytea, lambda { |s| s.gsub(/\\([0-3][0-7][0-7])/) { $1.to_i(8).chr }} ],
|
45
|
-
18 => [:rle_tuple, nil],
|
46
|
-
115 => [:long_varchar, STRING_CONVERTER],
|
47
|
-
}
|
48
|
-
|
49
|
-
DATA_TYPES = DATA_TYPE_CONVERSIONS.values.map { |t| t[0] }
|
50
|
-
|
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
|
55
|
-
|
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
|
1
|
+
# Class representing a column in a result.
|
2
|
+
#
|
3
|
+
# @attr_reader name [String] The name of the column
|
4
|
+
# @attr_reader table_oid [Integer, nil] The OID of the table this column originates from. This can
|
5
|
+
# be nil if the volumn was computed, and was not soruced from a table
|
6
|
+
# @attr_reader attribute_number [Integer, nil] The attribute index in the table this column originates from.
|
7
|
+
# This can be nil if the volumn was computed, and was not soruced from a table
|
8
|
+
# @attr_reader data_type [Vertica::DataType] The type of the values in this column.
|
9
|
+
#
|
10
|
+
# @see Vertica::RowDescription
|
11
|
+
# @see Vertica::DataType
|
12
|
+
class Vertica::Column
|
13
|
+
|
14
|
+
# Builds a new column instance based on the values provided by a {Vertica::Protocol::RowDescription} message.
|
15
|
+
# @param name [String] The name of the column
|
16
|
+
# @param table_oid [Integer, nil] The OID of the table this column originates from. This can
|
17
|
+
# be nil if the volumn was computed, and was not soruced from a table
|
18
|
+
# @param attribute_number [Integer, nil] The attribute index in the table this column originates from.
|
19
|
+
# This can be nil if the volumn was computed, and was not soruced from a table
|
20
|
+
# @param data_type_oid [Integer] The object ID of the type.
|
21
|
+
# @param data_type_size [Integer] The size of the type.
|
22
|
+
# @param data_type_modifier [Integer] A modifier of the type.
|
23
|
+
# @param data_format [Integer] The serialization format of this type.
|
24
|
+
# @return [Vertica::Column]
|
25
|
+
def self.build(name: nil, table_oid: nil, attribute_number: nil, data_format: 0, data_type_oid: nil, data_type_size: nil, data_type_modifier: nil)
|
26
|
+
data_type = Vertica::DataType.build(oid: data_type_oid, size: data_type_size, modifier: data_type_modifier, format: data_format)
|
27
|
+
new(name: name, data_type: data_type, table_oid: table_oid, attribute_number: attribute_number)
|
28
|
+
end
|
61
29
|
|
62
|
-
|
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
|
30
|
+
attr_reader :name, :data_type, :table_oid, :attribute_number
|
72
31
|
|
73
|
-
|
32
|
+
# Initializes a new Vertica::Column.
|
33
|
+
# @see .build
|
34
|
+
def initialize(name: nil, data_type: nil, table_oid: nil, attribute_number: nil)
|
35
|
+
@name = name
|
36
|
+
@table_oid = table_oid
|
37
|
+
@attribute_number = attribute_number
|
38
|
+
@data_type = data_type
|
39
|
+
end
|
74
40
|
|
75
|
-
|
76
|
-
|
77
|
-
|
41
|
+
# @return [Boolean] Returns true iff this record is equal to the other provided object
|
42
|
+
def eql?(other)
|
43
|
+
self.class === other && other.name == name && other.data_type == data_type &&
|
44
|
+
other.table_oid == table_oid && other.attribute_number == attribute_number
|
45
|
+
end
|
78
46
|
|
79
|
-
|
80
|
-
return unless s
|
81
|
-
@converter ? @converter.call(s) : s
|
82
|
-
end
|
47
|
+
alias_method :==, :eql?
|
83
48
|
|
84
|
-
|
49
|
+
# @return [Integer] Returns a hash digtest of this object.
|
50
|
+
def hash
|
51
|
+
[name, data_type, table_oid, attribute_number].hash
|
52
|
+
end
|
85
53
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
90
|
-
end
|
54
|
+
# @return [String] Returns a user-consumable string representation of this column.
|
55
|
+
def inspect
|
56
|
+
"#<#{self.class.name} name=#{name.inspect} data_type=#{data_type.inspect}>"
|
91
57
|
end
|
92
58
|
end
|
data/lib/vertica/connection.rb
CHANGED
@@ -1,12 +1,54 @@
|
|
1
1
|
require 'socket'
|
2
2
|
|
3
|
+
# A client for a Vertica server, which allows you to run queries against it.
|
4
|
+
#
|
5
|
+
# Use {Vertica.connect} to establish a connection. Then, the {#query} method will allow you
|
6
|
+
# to run SQL queries against the database. For `COPY FROM STDIN` queries, use the {#copy} method
|
7
|
+
# instead. You can use {#interrupt} to interrupt long running queries. {#close} will close the
|
8
|
+
# connection to the server.
|
9
|
+
#
|
10
|
+
# @attr_reader transaction_status [:no_transaction, :in_transaction, :failed_transaction] The current
|
11
|
+
# transaction state of the session. This will be updated after every query.
|
12
|
+
# @attr_reader parameters [Hash<String, String>] Connection parameters as provided by the server.
|
13
|
+
# @attr_reader options [Hash] The connection options provided to the constructor. See {#initialize}.
|
14
|
+
#
|
15
|
+
# @example Running a buffered query against the database
|
16
|
+
# connection = Vertica.connect(host: 'db_server', username: 'user', password: 'password', ...)
|
17
|
+
# result = connection.query("SELECT id, name FROM my_table")
|
18
|
+
# result.each do |row|
|
19
|
+
# puts "Row: #row['id']: #{row['name']}"
|
20
|
+
# end
|
21
|
+
# connection.close
|
22
|
+
#
|
23
|
+
# @see Vertica.connect
|
24
|
+
# @see Vertica::Result
|
3
25
|
class Vertica::Connection
|
4
26
|
|
5
27
|
attr_reader :transaction_status, :parameters, :options
|
6
28
|
|
7
|
-
#
|
8
|
-
# @param [
|
9
|
-
|
29
|
+
# Creates a connection the a Vertica server.
|
30
|
+
# @param host [String] The hostname to connect to. E.g. `localhost`
|
31
|
+
# @param port [Integer] The port to connect to. Defaults to `5433`.
|
32
|
+
# @param username [String] The username for the session.
|
33
|
+
# @param password [String] The password for the session.
|
34
|
+
# @param interruptable [true, false] Whether to make this session interruptible. Setting this to true
|
35
|
+
# allows you to interrupt sessions and queries, but requires running a query during startup in order
|
36
|
+
# to obtain the session id.
|
37
|
+
# @param ssl [OpenSSL::SSL::SSLContext, Boolean] Set this to an OpenSSL::SSL::SSLContext instance to
|
38
|
+
# require the connection to be encrypted using SSL/TLS. `true` will use the default SSL options.
|
39
|
+
# Not every server has support for SSL encryption. In that case you'll have to leave this to false.
|
40
|
+
# @param read_timeout [Integer] The number of seconds to wait for data on the connection. You should
|
41
|
+
# set this to a sufficiently high value when executing complicated queries that require a long time
|
42
|
+
# to be evaluated.
|
43
|
+
# @param role [Array<String>, :all, :none, :default] A list of additional roles to enable for the session. See the
|
44
|
+
# [Vertica documentation for `SET ROLE`](https://my.vertica.com/docs/7.1.x/HTML/Content/Authoring/SQLReferenceManual/Statements/SET/SETROLE.htm).
|
45
|
+
# @param timezone [String] The timezone to use for the session. See the
|
46
|
+
# [Vertica documentation for `SET TIME ZONE`](https://my.vertica.com/docs/7.1.x/HTML/Content/Authoring/SQLReferenceManual/Statements/SET/SETTIMEZONE.htm).
|
47
|
+
# @param search_path [Array<String>] A list of schemas to use as search path. See the
|
48
|
+
# [Vertica documentation for `SET SEARCH_PATH`](https://my.vertica.com/docs/7.1.x/HTML/Content/Authoring/SQLReferenceManual/Statements/SET/SETSEARCH_PATH.htm).
|
49
|
+
# @param debug [Boolean] Setting this to true will log all the communication between client and server
|
50
|
+
# to STDOUT. Useful when developing this library.
|
51
|
+
def initialize(host: nil, port: 5433, username: nil, password: nil, database: nil, interruptable: false, ssl: false, read_timeout: 600, debug: false, role: nil, search_path: nil, timezone: nil, autocommit: false, skip_startup: false, skip_initialize: false, user: nil)
|
10
52
|
reset_state
|
11
53
|
@notice_handler = nil
|
12
54
|
|
@@ -29,41 +71,110 @@ class Vertica::Connection
|
|
29
71
|
boot_connection(skip_initialize: skip_initialize) unless skip_startup
|
30
72
|
end
|
31
73
|
|
32
|
-
|
33
|
-
@notice_handler = block
|
34
|
-
end
|
35
|
-
|
74
|
+
# @return [Boolean] Returns true iff the connection is encrypted.
|
36
75
|
def ssl?
|
37
76
|
Object.const_defined?('OpenSSL') && @socket.kind_of?(OpenSSL::SSL::SSLSocket)
|
38
77
|
end
|
39
78
|
|
79
|
+
# @return [Boolean] Returns true iff the connection to the server is opened.
|
80
|
+
# @note The connection will be opened automatically if you use it.
|
40
81
|
def opened?
|
41
82
|
@socket && @backend_pid && @transaction_status
|
42
83
|
end
|
43
84
|
|
85
|
+
# @return [Boolean] Returns false iff the connection to the server is opened.
|
86
|
+
# @note Even if the connection is closed, it will be opened automatically if you use it.
|
44
87
|
def closed?
|
45
88
|
!opened?
|
46
89
|
end
|
47
90
|
|
91
|
+
# @return [Boolean] Returns true iff the connection is in use.
|
48
92
|
def busy?
|
49
93
|
@mutex.locked?
|
50
94
|
end
|
51
95
|
|
96
|
+
# @return [Boolean] Returns true iff the connection is ready to handle queries.
|
52
97
|
def ready_for_query?
|
53
98
|
!busy?
|
54
99
|
end
|
55
100
|
|
101
|
+
# Returns true iff the connection can be interrupted.
|
102
|
+
#
|
103
|
+
# Connections can only be interrupted if the session ID is known, so it can
|
104
|
+
# run `SELECT CLOSE_SESSION(session_id)` using a separate connection. By passing
|
105
|
+
# `interruptable: true` as a connection parameter (see {#initialize}), the connection
|
106
|
+
# will discover its session id before you can use it, allowing it to be interrupted.
|
107
|
+
#
|
108
|
+
# @return [Boolean] Returns true iff the connection can be interrupted.
|
109
|
+
# @see {#interrupt}
|
56
110
|
def interruptable?
|
57
111
|
!session_id.nil?
|
58
112
|
end
|
59
113
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
114
|
+
# Runs a SQL query against the database.
|
115
|
+
#
|
116
|
+
# @overload query(sql)
|
117
|
+
# Runs a query against the database, and return the full result as a {Vertica::Result}
|
118
|
+
# instance.
|
119
|
+
#
|
120
|
+
# @note This requires the entire result to be buffered in memory, which may cause problems
|
121
|
+
# for queries with large results. Consider using the unbuffered version instead.
|
122
|
+
#
|
123
|
+
# @param sql [String] The SQL command to run.
|
124
|
+
# @return [Vertica::Result]
|
125
|
+
# @raise [Vertica::Error::ConnectionError] The connection to the server failed.
|
126
|
+
# @raise [Vertica::Error::QueryError] The server sent an error response indicating that
|
127
|
+
# the provided query cannot be evaluated.
|
128
|
+
#
|
129
|
+
# @overload query(sql, &block)
|
130
|
+
# Runs a query against the database, and yield every {Vertica::Row row} to the provided
|
131
|
+
# block.
|
132
|
+
#
|
133
|
+
# @param sql [String] The SQL command to run.
|
134
|
+
# @yield The provided block will be called for every row in the result.
|
135
|
+
# @yieldparam row [Vertica::Row]
|
136
|
+
# @return [String] The kind of command that was executed, e.g. `"SELECT"`.
|
137
|
+
# @raise [Vertica::Error::ConnectionError] The connection to the server failed.
|
138
|
+
# @raise [Vertica::Error::QueryError] The server sent an error response indicating that
|
139
|
+
# the provided query cannot be evaluated.
|
140
|
+
#
|
141
|
+
# @see https://my.vertica.com/docs/7.1.x/HTML/Content/Authoring/SQLReferenceManual/Statements/SELECT/SELECT.htm
|
142
|
+
# Vertica's documentation for SELECT.
|
143
|
+
def query(sql, &block)
|
144
|
+
run_in_mutex(Vertica::Query.new(self, sql, row_handler: block))
|
145
|
+
end
|
146
|
+
|
147
|
+
# Loads data into Vertica using a `COPY table FROM STDIN` query.
|
148
|
+
#
|
149
|
+
# @param sql [String] The `COPY ... FROM STDIN` SQL command to run.
|
150
|
+
# @param source [String, IO] The source of the data to be copied. This can either be a filename, or
|
151
|
+
# an IO object. If you don't specify a source, you'll need to provide a block that will provide the
|
152
|
+
# data to be copied.
|
153
|
+
# @yield A block that will be called with a writer that you can provided data to. If an exception is
|
154
|
+
# raised in the block, the `COPY` command will be cancelled.
|
155
|
+
# @yieldparam io [:write] An object that you can call write on to provide data to be loaded.
|
156
|
+
# @return [String] The kind of command that was executed on the server. This should always be `"COPY"`.
|
157
|
+
#
|
158
|
+
# @example Loading data using an IO object as source
|
159
|
+
# connection = Vertica.connect(host: 'db_server', username: 'user', password: 'password', ...)
|
160
|
+
# File.open("filename.csv", "r") do |io|
|
161
|
+
# connection.copy("COPY my_table FROM STDIN ...", source: io)
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# @example Loading data using a filename as source
|
165
|
+
# connection = Vertica.connect(host: 'db_server', username: 'user', password: 'password', ...)
|
166
|
+
# connection.copy("COPY my_table FROM STDIN ...", source: "filename.csv")
|
167
|
+
#
|
168
|
+
# @example Loading data using a callback
|
169
|
+
# connection = Vertica.connect(host: 'db_server', username: 'user', password: 'password', ...)
|
170
|
+
# connection.copy("COPY my_table FROM STDIN ...") do |io|
|
171
|
+
# io.write("my data")
|
172
|
+
# io.write("more data")
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# @see https://my.vertica.com/docs/7.1.x/HTML/Content/Authoring/SQLReferenceManual/Statements/COPY/COPY.htm
|
176
|
+
# Vertica's documentation for COPY.
|
177
|
+
def copy(sql, source: nil, &block)
|
67
178
|
copy_handler = if block_given?
|
68
179
|
block
|
69
180
|
elsif source && File.exist?(source.to_s)
|
@@ -72,22 +183,33 @@ class Vertica::Connection
|
|
72
183
|
lambda { |data| io_copy_handler(source, data) }
|
73
184
|
end
|
74
185
|
|
75
|
-
|
76
|
-
|
77
|
-
run_with_mutex(job)
|
186
|
+
run_in_mutex(Vertica::Query.new(self, sql, copy_handler: copy_handler))
|
78
187
|
end
|
79
188
|
|
189
|
+
# Returns a user-consumable string representation of this row.
|
190
|
+
# @return [String]
|
80
191
|
def inspect
|
81
192
|
safe_options = @options.reject { |name, _| name == :password }
|
82
193
|
"#<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}>"
|
83
194
|
end
|
84
195
|
|
196
|
+
# Closes the connection to the Vertica server.
|
197
|
+
# @return [void]
|
85
198
|
def close
|
86
199
|
write_message(Vertica::Protocol::Terminate.new)
|
87
200
|
ensure
|
88
201
|
close_socket
|
89
202
|
end
|
90
203
|
|
204
|
+
# Cancels the current query.
|
205
|
+
#
|
206
|
+
# @note Vertica's protocol is based on the PostgreSQL protocol. This method to cancel sessions
|
207
|
+
# in PostgreSQL is accepted by the Vertica server, but I haven't actually observed queries
|
208
|
+
# actually being cancelled when using this method. Vertica provides an alternative method, by
|
209
|
+
# running `SELECT CLOSE_SESSION(session_id)` as a query on a different connection. See {#interrupt}.
|
210
|
+
#
|
211
|
+
# @return [void]
|
212
|
+
# @see #interrupt
|
91
213
|
def cancel
|
92
214
|
conn = self.class.new(skip_startup: true, **options)
|
93
215
|
conn.write_message(Vertica::Protocol::CancelRequest.new(backend_pid, backend_key))
|
@@ -95,6 +217,18 @@ class Vertica::Connection
|
|
95
217
|
conn.close_socket
|
96
218
|
end
|
97
219
|
|
220
|
+
# Interrupts this session to the Vertica server, which will cancel the running query.
|
221
|
+
#
|
222
|
+
# You'll have to call this method in a separate thread. It will open up a separate connection, and run
|
223
|
+
# `SELECT CLOSE_SESSION(current_session_id)` to close the current session. In order to be able to do this
|
224
|
+
# the client needs to know its session ID. You'll have to pass `interruptable: true` as a connection
|
225
|
+
# parameter (see {#initialize}) to make sure the connection will request its session id, by running
|
226
|
+
# `SELECT session_id FROM v_monitor.current_session` right after the connection is opened.
|
227
|
+
#
|
228
|
+
# @return [void]
|
229
|
+
# @see #interruptable?
|
230
|
+
# @see https://my.vertica.com/docs/7.1.x/HTML/Content/Authoring/SQLReferenceManual/Functions/VerticaFunctions/CLOSE_SESSION.htm
|
231
|
+
# Vertica's documentation for CLOSE_SESSION
|
98
232
|
def interrupt
|
99
233
|
raise Vertica::Error::InterruptImpossible, "Session cannopt be interrupted because the session ID is not known!" if session_id.nil?
|
100
234
|
conn = self.class.new(skip_initialize: true, **options)
|
@@ -103,6 +237,20 @@ class Vertica::Connection
|
|
103
237
|
conn.close if conn
|
104
238
|
end
|
105
239
|
|
240
|
+
# Installs a hanlder for notices that may be sent from the server to the client.
|
241
|
+
#
|
242
|
+
# You can only install one handler; if you call this method again it will replace the
|
243
|
+
# previous handler.
|
244
|
+
#
|
245
|
+
# @return [void]
|
246
|
+
def on_notice(&block)
|
247
|
+
@notice_handler = block
|
248
|
+
end
|
249
|
+
|
250
|
+
# Writes a frontend message to the socket.
|
251
|
+
# @note This method is for internal use only; you should not call it directly.
|
252
|
+
# @return [void]
|
253
|
+
# @raise [Vertica::Error::ConnectionError]
|
106
254
|
# @private
|
107
255
|
def write_message(message)
|
108
256
|
puts "=> #{message.inspect}" if options.fetch(:debug)
|
@@ -112,6 +260,10 @@ class Vertica::Connection
|
|
112
260
|
raise Vertica::Error::ConnectionError.new(e.message)
|
113
261
|
end
|
114
262
|
|
263
|
+
# Reads a backend message from the socket.
|
264
|
+
# @note This method is for internal use only; you should not call it directly.
|
265
|
+
# @return [Vertica::Protocol::BackendMessage]
|
266
|
+
# @raise [Vertica::Error::ConnectionError]
|
115
267
|
# @private
|
116
268
|
def read_message
|
117
269
|
type, size = read_bytes(5).unpack('aN')
|
@@ -124,6 +276,9 @@ class Vertica::Connection
|
|
124
276
|
raise Vertica::Error::ConnectionError.new(e.message)
|
125
277
|
end
|
126
278
|
|
279
|
+
# Processes a backend message that was received from the socket.
|
280
|
+
# @note This method is for internal use only; you should not call it directly.
|
281
|
+
# @return [void]
|
127
282
|
# @private
|
128
283
|
def process_message(message)
|
129
284
|
case message
|
@@ -168,7 +323,7 @@ class Vertica::Connection
|
|
168
323
|
end
|
169
324
|
end
|
170
325
|
|
171
|
-
def
|
326
|
+
def run_in_mutex(job)
|
172
327
|
boot_connection if closed?
|
173
328
|
if @mutex.try_lock
|
174
329
|
begin
|