vertica 1.0.0.rc1 → 1.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|