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