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.
@@ -1,10 +1,21 @@
1
+ # Class that describes the shape of a query result. It contains an ordered list
2
+ # of {Vertica::Column} instances, which describes the columns that will apear for
3
+ # every {Vertica::Row} part of the result of a query.
4
+ #
5
+ # @see Vertica::Row
6
+ # @see Vertica::Result
1
7
  class Vertica::RowDescription
2
8
  include Enumerable
3
9
 
10
+ # Builds a new Vertica::RowDescription instance given a list of columns.
11
+ # @param columns [Vertica::Protocol::RowDescription, Vertica::RowDescription, Array<Vertica::Column>]
12
+ # An object that describes the list of columns.
13
+ # @return [Vertica::RowDescription]
14
+ # @raise [ArgumentError] If no Vertica::RowDescription could be constructed given the provided argument.
4
15
  def self.build(columns)
5
16
  case columns
6
17
  when Vertica::Protocol::RowDescription
7
- new(columns.fields.map { |fd| Vertica::Column.new(fd) })
18
+ new(columns.fields.map { |fd| Vertica::Column.build(fd) })
8
19
  when Vertica::RowDescription
9
20
  columns
10
21
  when Array
@@ -16,49 +27,84 @@ class Vertica::RowDescription
16
27
  end
17
28
  end
18
29
 
30
+ # @param columns [Array<Vertica::Column>] The list of columns as they will
31
+ # appear in the rows of a query result.
32
+ # @see Vertica::RowDescription.build
19
33
  def initialize(columns)
20
34
  @columns = columns
21
35
  end
22
36
 
37
+ # Returns a column in this row description.
38
+ # @param name_or_index [String, Symbol, Integer] The name of the column, or
39
+ # the index of the column in this row description.
40
+ # @return [Vertica::Column]
41
+ # @raise [KeyError] if the column could not be found in this row description.
23
42
  def column(name_or_index)
24
43
  column_with_index(name_or_index).first
25
44
  end
26
45
 
27
46
  alias_method :[], :column
28
47
 
48
+ # Returns a column, accompanied by the index of that column in this row description.
49
+ # @param name_or_index [String, Symbol, Integer] The name of the column, or
50
+ # the index of the column in this row description.
51
+ # @return [Array<Vertica::Column, Integer>]
52
+ # @raise [KeyError] if the column could not be found in this row description.
29
53
  def column_with_index(name_or_index)
30
54
  columns_index.fetch(name_or_index)
31
55
  end
32
56
 
57
+ # Iterates over the columns in this row description.
58
+ # @yield The provided block will be called with every column.
59
+ # @yieldparam column [Vertica::Column]
60
+ # @return [void]
33
61
  def each(&block)
34
62
  @columns.each(&block)
35
63
  end
36
64
 
65
+ # @return [Integer] Returns the number of columns in this row description.
37
66
  def size
38
67
  @columns.size
39
68
  end
40
69
 
41
70
  alias_method :length, :size
42
71
 
72
+ # @return [Array<Vertica::Column>] Returns the columns of this row description as an array.
43
73
  def to_a
44
74
  @columns.clone
45
75
  end
46
76
 
77
+ # @param symbolize_keys [Boolean] Whether to use symbols instead of strings as keys.
78
+ # @return [Hash] Returns the columns of this row description as a hash, index by the
79
+ # column name.
80
+ # @raise [Vertica::Error::DuplicateColumnName] If the row description contains multiple
81
+ # columns with the same name
47
82
  def to_h(symbolize_keys: false)
48
83
  @columns.inject({}) do |carry, column|
49
84
  key = symbolize_keys ? column.name.to_sym : column.name
50
- carry.merge(key => column)
85
+ raise Vertica::Error::DuplicateColumnName, "Column with name #{key} occurs more than once in this row description." if carry.key?(key)
86
+ carry[key] = column
87
+ carry
51
88
  end
52
89
  end
53
90
 
91
+ # Builds a {Vertica::Row} instance given a list of values that conforms to
92
+ # this row description.
93
+ # @param [Array, Hash, Vertica::Protocol::DataRow] values A list of values. The number
94
+ # of values should match the number of columns in the row description.
95
+ # @raise [ArgumentError] An ArgumentErrpr is raised if the number of values does not match
96
+ # the row description, or if a type is provided that it cannot handle.
97
+ # @return [Vertica::Row] The row instance.
54
98
  def build_row(values)
55
99
  case values
56
100
  when Vertica::Row
101
+ raise ArgumentError, "Row description of provided row does match this row description" if values.row_description != self
57
102
  values
58
103
 
59
104
  when Vertica::Protocol::DataRow
105
+ raise ArgumentError, "Number of values does not match row description" if values.values.size != size
60
106
  converted_values = @columns.map.with_index do |column, index|
61
- column.convert(values.values.fetch(index))
107
+ column.data_type.deserialize(values.values.fetch(index))
62
108
  end
63
109
  Vertica::Row.new(self, converted_values)
64
110
 
@@ -76,16 +122,23 @@ class Vertica::RowDescription
76
122
  end
77
123
  end
78
124
 
125
+ # @return [Boolean] Returns true iff this record is equal to the other provided object
79
126
  def eql?(other)
80
127
  self.class === other && other.to_a == self.to_a
81
128
  end
82
129
 
83
130
  alias_method :==, :eql?
84
131
 
132
+ # @return [Integer] Returns a hash digtest of this object.
85
133
  def hash
86
134
  @columns.hash
87
135
  end
88
136
 
137
+ # @return [String] Returns a user-consumable string representation of this row description.
138
+ def inspect
139
+ "#<#{self.class.name}[#{@columns.map(&:name).join(', ')}]>"
140
+ end
141
+
89
142
  protected
90
143
 
91
144
  def columns_index
@@ -1,3 +1,5 @@
1
1
  module Vertica
2
- VERSION = "1.0.0.rc1"
2
+ # The version of the package. We adhere to semantic versioning.
3
+ # To release a new version, update this constant, commit to master, and run `rake release`
4
+ VERSION = "1.0.0.rc2"
3
5
  end
@@ -16,7 +16,7 @@ class FunctionalConnectionTest < Minitest::Test
16
16
 
17
17
  connection.close
18
18
  assert_valid_closed_connection(connection)
19
- assert !connection.ssl?
19
+ refute connection.ssl?
20
20
 
21
21
  rescue Vertica::Error::SSLNotSupported
22
22
  puts "\nThe test server doesn't support SSL, so SSL connections could not be tested."
@@ -93,7 +93,7 @@ class FunctionalConnectionTest < Minitest::Test
93
93
  TCPSocket.any_instance.expects(:read_nonblock).raises(Errno::ETIMEDOUT)
94
94
 
95
95
  assert_raises(Vertica::Error::ConnectionError) { Vertica::Connection.new(TEST_CONNECTION_HASH) }
96
- end
96
+ end
97
97
 
98
98
  def test_socket_write_error_during_query
99
99
  connection = Vertica::Connection.new(TEST_CONNECTION_HASH)
@@ -23,9 +23,9 @@ class FunctionalQueryTest < Minitest::Test
23
23
  r = @connection.query("SELECT * FROM test_ruby_vertica_table")
24
24
  assert_equal 1, r.size
25
25
  assert_equal 2, r.row_description.length
26
- assert_equal :integer, r.columns[0].data_type
26
+ assert_equal 'integer', r.columns[0].data_type.name
27
27
  assert_equal 'id', r.columns[0].name
28
- assert_equal :varchar, r.columns[1].data_type
28
+ assert_equal 'varchar', r.columns[1].data_type.name
29
29
  assert_equal 'name', r.columns[1].name
30
30
 
31
31
  assert_equal 1, r[0][:id]
@@ -36,6 +36,7 @@ class FunctionalQueryTest < Minitest::Test
36
36
  rows = []
37
37
  result = @connection.query("SELECT 1 AS a, 2 AS b UNION ALL SELECT 3, 4") do |row|
38
38
  assert_kind_of Vertica::Row, row
39
+ assert_kind_of Vertica::RowDescription, row.row_description
39
40
  rows << row
40
41
  end
41
42
 
@@ -64,11 +65,11 @@ class FunctionalQueryTest < Minitest::Test
64
65
  assert_equal "SELECT", r.tag
65
66
  assert_equal 0, r.size
66
67
  assert_equal 2, r.columns.length
67
- assert_equal :integer, r.columns[0].data_type
68
+ assert_equal 'integer', r.columns[0].data_type.name
68
69
  assert_equal 'id', r.columns[0].name
69
- assert_equal :varchar, r.columns[1].data_type
70
+ assert_equal 'varchar', r.columns[1].data_type.name
70
71
  assert_equal 'name', r.columns[1].name
71
- assert_empty r.rows
72
+ assert_empty r
72
73
  end
73
74
 
74
75
  def test_insert
@@ -76,7 +77,7 @@ class FunctionalQueryTest < Minitest::Test
76
77
  assert_equal "INSERT", r.tag
77
78
  assert_equal 1, r.size
78
79
  assert_equal 1, r.columns.length
79
- assert_equal :integer, r.columns[0].data_type
80
+ assert_equal 'integer', r.columns[0].data_type.name
80
81
  assert_equal 'OUTPUT', r.columns[0].name
81
82
  assert_equal 1, r.value
82
83
  end
@@ -87,7 +88,7 @@ class FunctionalQueryTest < Minitest::Test
87
88
  assert_equal "DELETE", r.tag
88
89
  assert_equal 1, r.size
89
90
  assert_equal 1, r.columns.length
90
- assert_equal :integer, r.columns[0].data_type
91
+ assert_equal 'integer', r.columns[0].data_type.name
91
92
  assert_equal 'OUTPUT', r.columns[0].name
92
93
  assert_equal 0, r.value
93
94
  end
@@ -97,7 +98,7 @@ class FunctionalQueryTest < Minitest::Test
97
98
  assert_equal "DELETE", r.tag
98
99
  assert_equal 1, r.size
99
100
  assert_equal 1, r.columns.length
100
- assert_equal :integer, r.columns[0].data_type
101
+ assert_equal 'integer', r.columns[0].data_type.name
101
102
  assert_equal 'OUTPUT', r.columns[0].name
102
103
  assert_equal 1, r.value
103
104
  end
@@ -119,9 +120,9 @@ class FunctionalQueryTest < Minitest::Test
119
120
  r = @connection.query("SELECT * FROM test_ruby_vertica_table")
120
121
  assert_equal 1, r.size
121
122
  assert_equal 2, r.columns.length
122
- assert_equal :integer, r.columns[0].data_type
123
+ assert_equal 'integer', r.columns[0].data_type.name
123
124
  assert_equal 'id', r.columns[0].name
124
- assert_equal :varchar, r.columns[1].data_type
125
+ assert_equal 'varchar', r.columns[1].data_type.name
125
126
  assert_equal 'name', r.columns[1].name
126
127
  assert_equal [{'id' => 1, 'name' => "matt"}], r.map(&:to_h)
127
128
  end
@@ -156,11 +157,13 @@ class FunctionalQueryTest < Minitest::Test
156
157
  end
157
158
 
158
159
  def test_copy_in_with_customer_handler
159
- @connection.copy("COPY test_ruby_vertica_table FROM STDIN") do |data|
160
+ copy_result = @connection.copy("COPY test_ruby_vertica_table FROM STDIN") do |data|
160
161
  data.write "11|Stuff\r\n"
161
162
  data << "12|More stuff\n13|Fin" << "al stuff\n"
162
163
  end
163
164
 
165
+ assert_equal "COPY", copy_result
166
+
164
167
  result = @connection.query("SELECT * FROM test_ruby_vertica_table ORDER BY id")
165
168
  assert_equal 4, result.length
166
169
  assert_equal [[1, "matt"], [11, "Stuff"], [12, "More stuff"], [13, "Final stuff"]], result.map(&:to_a)
@@ -0,0 +1,179 @@
1
+ # encoding : UTF-8
2
+ require 'test_helper'
3
+
4
+ class FunctionalTypeDeserializationTest < Minitest::Test
5
+
6
+ def setup
7
+ @connection = Vertica::Connection.new(TEST_CONNECTION_HASH)
8
+
9
+ @connection.query <<-SQL
10
+ CREATE TABLE IF NOT EXISTS conversions_table (
11
+ "int_field" int,
12
+ "varchar_field" varchar(100),
13
+ "char_field" char(10),
14
+ "long_varchar_field" long varchar,
15
+ "date_field" date,
16
+ "timestamp_field" timestamp,
17
+ "timestamptz_field" timestamptz,
18
+ "time_field" time,
19
+ "interval_field" interval,
20
+ "boolean_field" boolean,
21
+ "float_field" float,
22
+ "numeric_field" numeric(10, 2),
23
+ "binary_field" varbinary
24
+ )
25
+ SQL
26
+ end
27
+
28
+
29
+ def teardown
30
+ @connection.query("DROP TABLE IF EXISTS conversions_table CASCADE;")
31
+ @connection.close
32
+ end
33
+
34
+ def test_deserialize_values_from_table
35
+ @connection.query <<-SQL
36
+ INSERT INTO conversions_table VALUES (
37
+ 123,
38
+ 'hello world',
39
+ 'hello',
40
+ 'hello world',
41
+ '2010-01-01',
42
+ '2010-01-01 12:00:00.123456',
43
+ '2010-01-01 12:00:00 +0930',
44
+ '12:00:00',
45
+ INTERVAL '1 DAY',
46
+ TRUE,
47
+ -1.123,
48
+ 1.12345,
49
+ HEX_TO_BINARY('d09fd180d0b8d0b2d0b5d1822c2068656c6c6f21')
50
+ )
51
+ SQL
52
+
53
+ result = @connection.query("SELECT * FROM conversions_table LIMIT 1")
54
+
55
+ assert_equal 1, result.size
56
+ assert_equal 123, result.fetch(0, 'int_field')
57
+ assert_equal 'hello world', result.fetch(0, 'varchar_field')
58
+ assert_equal 'hello ', result.fetch(0, 'char_field')
59
+ assert_equal 'hello world', result.fetch(0, 'long_varchar_field')
60
+ assert_equal Date.parse('2010-01-01'), result.fetch(0, 'date_field')
61
+ assert_equal Time.new(2010, 1, 1, 12, 0, BigDecimal.new("0.123456")), result.fetch(0, 'timestamp_field')
62
+ assert_equal Time.new(2010, 1, 1, 12, 0, 0, '+09:30'), result.fetch(0, 'timestamptz_field')
63
+ assert_equal "12:00:00", result.fetch(0, 'time_field')
64
+ assert_equal "1", result.fetch(0, 'interval_field')
65
+ assert_equal true, result.fetch(0, 'boolean_field')
66
+ assert_equal -1.123, result.fetch(0, 'float_field')
67
+ assert_equal BigDecimal.new('1.12'), result.fetch(0, 'numeric_field')
68
+ assert_equal ['d09fd180d0b8d0b2d0b5d1822c2068656c6c6f21'].pack('H*'), result.fetch(0, 'binary_field')
69
+ end
70
+
71
+ def test_nil_values_from_table
72
+ @connection.query("INSERT INTO conversions_table VALUES (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)")
73
+ result = @connection.query("SELECT * FROM conversions_table LIMIT 1")
74
+ assert_equal 1, result.size
75
+ assert result[0].all?(&:nil?)
76
+ end
77
+
78
+ def test_deserialize_float_values
79
+ result = @connection.query(<<-SQL)
80
+ SELECT 0::float,
81
+ -0::float,
82
+ 1.1::float,
83
+ -1.1::float,
84
+ 1.0::float / 0.0::float,
85
+ -1.0::float / 0.0::float,
86
+ 1.0::float / 0.0::float - 1.0::float / 0.0::float,
87
+ NULL::float
88
+ SQL
89
+
90
+ assert_equal 1, result.size
91
+ assert_equal 0.0, result[0, 0]
92
+ assert_equal -0.0, result[0, 1]
93
+ assert_equal 1.1, result[0, 2]
94
+ assert_equal -1.1, result[0, 3]
95
+ assert_equal Float::INFINITY, result[0, 4]
96
+ assert_equal -Float::INFINITY, result[0, 5]
97
+ assert result[0, 6].equal?(Float::NAN)
98
+ assert_nil result[0, 7]
99
+ end
100
+
101
+ def test_deserialize_numeric_values
102
+ result = @connection.query(<<-SQL)
103
+ SELECT '1'::numeric(5,2),
104
+ '1.12'::numeric(5,2),
105
+ '1.1234'::numeric(5,2),
106
+ '1.1234'::numeric,
107
+ '0'::numeric,
108
+ '-0'::numeric,
109
+ NULL::numeric
110
+ SQL
111
+
112
+ assert_equal 1, result.size
113
+ assert_equal BigDecimal.new('1.00'), result.fetch(0, 0)
114
+ assert_equal BigDecimal.new('1.12'), result.fetch(0, 1)
115
+ assert_equal BigDecimal.new('1.12'), result.fetch(0, 2)
116
+ assert_equal BigDecimal.new('1.1234'), result.fetch(0, 3)
117
+ assert_equal BigDecimal.new('0'), result.fetch(0, 4)
118
+ assert_equal BigDecimal.new('-0'), result.fetch(0, 5)
119
+ assert_nil result.fetch(0, 6)
120
+ end
121
+
122
+ def test_deserialize_string_values
123
+ assert_equal 'åßç∂ë', @connection.query("SELECT 'åßç∂ë'").the_value
124
+ assert_equal Encoding::UTF_8, @connection.query("SELECT 'åßç∂ë'").the_value.encoding
125
+ end
126
+
127
+ def test_deserialize_binary_values
128
+ assert_equal ['d09fd180d0b8d0b2d0b5d1822c2068656c6c6f21'].pack('H*'), @connection.query("SELECT HEX_TO_BINARY('d09fd180d0b8d0b2d0b5d1822c2068656c6c6f21')").the_value
129
+ assert_equal Encoding::BINARY, @connection.query("SELECT HEX_TO_BINARY('d09fd180d0b8d0b2d0b5d1822c2068656c6c6f21')").the_value.encoding
130
+ end
131
+
132
+ def test_deserialize_timestamp_values_with_utc_timezone_connection
133
+ @connection.query("SET TIMEZONE TO 'UTC'")
134
+
135
+ result = @connection.query(<<-SQL)
136
+ SELECT '2013-01-02 14:15:16'::timestamp,
137
+ '2013-01-02 14:15:16 America/Toronto'::timestamp,
138
+ '2013-01-02 14:15:16 +9:30'::timestamp,
139
+ '2013-01-02 14:15:16'::timestamptz,
140
+ '2013-01-02 14:15:16 America/Toronto'::timestamptz,
141
+ '2013-01-02 14:15:16 +9:30'::timestamptz,
142
+ NULL::timestamp,
143
+ NULL::timestamptz
144
+ SQL
145
+
146
+ assert_equal Time.new(2013, 1, 2, 14, 15, 16), result.fetch(0, 0)
147
+ assert_equal Time.new(2013, 1, 2, 19, 15, 16), result.fetch(0, 1)
148
+ assert_equal Time.new(2013, 1, 2, 4, 45, 16), result.fetch(0, 2)
149
+ assert_equal Time.new(2013, 1, 2, 14, 15, 16, '+00:00'), result.fetch(0, 3)
150
+ assert_equal Time.new(2013, 1, 2, 19, 15, 16, '+00:00'), result.fetch(0, 4)
151
+ assert_equal Time.new(2013, 1, 2, 4, 45, 16, '+00:00'), result.fetch(0, 5)
152
+ assert_nil result.fetch(0, 6)
153
+ assert_nil result.fetch(0, 7)
154
+ end
155
+
156
+ def test_deserialize_timestamp_values_with_toronto_timezone_connection
157
+ @connection.query("SET TIMEZONE TO 'America/Toronto'")
158
+
159
+ result = @connection.query(<<-SQL)
160
+ SELECT '2013-01-02 14:15:16'::timestamp,
161
+ '2013-01-02 14:15:16 America/Toronto'::timestamp,
162
+ '2013-01-02 14:15:16 +9:30'::timestamp,
163
+ '2013-01-02 14:15:16'::timestamptz,
164
+ '2013-01-02 14:15:16 America/Toronto'::timestamptz,
165
+ '2013-01-02 14:15:16 +9:30'::timestamptz,
166
+ NULL::timestamp,
167
+ NULL::timestamptz
168
+ SQL
169
+
170
+ assert_equal Time.new(2013, 1, 2, 14, 15, 16), result.fetch(0, 0)
171
+ assert_equal Time.new(2013, 1, 2, 14, 15, 16), result.fetch(0, 1)
172
+ assert_equal Time.new(2013, 1, 1, 23, 45, 16), result.fetch(0, 2)
173
+ assert_equal Time.new(2013, 1, 2, 14, 15, 16, '-05:00'), result.fetch(0, 3)
174
+ assert_equal Time.new(2013, 1, 2, 14, 15, 16, '-05:00'), result.fetch(0, 4)
175
+ assert_equal Time.new(2013, 1, 1, 23, 45, 16, '-05:00'), result.fetch(0, 5)
176
+ assert_nil result.fetch(0, 6)
177
+ assert_nil result.fetch(0, 7)
178
+ end
179
+ end
@@ -74,7 +74,7 @@ class BackendMessageTest < Minitest::Test
74
74
  :data_type_oid => 6,
75
75
  :data_type_size => 8,
76
76
  :data_type_modifier => 8,
77
- :format_code => 0
77
+ :data_format => 0
78
78
  }
79
79
 
80
80
  msg = Vertica::Protocol::RowDescription.new("\x00\x02id\x00\x00\np8\x00\x01\x00\x00\x00\x06\x00\b\xFF\xFF\xFF\xFF\x00\x00name\x00\x00\np8\x00\x02\x00\x00\x00\t\xFF\xFF\x00\x00\x00h\x00\x00")
@@ -85,7 +85,7 @@ class BackendMessageTest < Minitest::Test
85
85
  :data_type_oid => 6,
86
86
  :data_type_size => 8,
87
87
  :data_type_modifier => 4294967295,
88
- :format_code => 0
88
+ :data_format => 0
89
89
  }
90
90
  assert_equal msg.fields[1], {
91
91
  :name => "name",
@@ -94,7 +94,7 @@ class BackendMessageTest < Minitest::Test
94
94
  :data_type_oid => 9,
95
95
  :data_type_size => 65535,
96
96
  :data_type_modifier => 104,
97
- :format_code => 0
97
+ :data_format => 0
98
98
  }
99
99
  end
100
100
 
@@ -112,7 +112,5 @@ class BackendMessageTest < Minitest::Test
112
112
  def test_command_complete_message
113
113
  msg = Vertica::Protocol::CommandComplete.new("CREATE TABLE\x00")
114
114
  assert_equal "CREATE TABLE", msg.tag
115
- assert_nil msg.rows
116
- assert_nil msg.oid
117
115
  end
118
116
  end