pg 0.18.0 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +1221 -4
- data/History.rdoc +200 -0
- data/Manifest.txt +5 -18
- data/README-Windows.rdoc +15 -26
- data/README.rdoc +27 -10
- data/Rakefile +33 -24
- data/Rakefile.cross +57 -39
- data/ext/errorcodes.def +37 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +16 -1
- data/ext/extconf.rb +29 -35
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +27 -39
- data/ext/pg.c +27 -53
- data/ext/pg.h +66 -83
- data/ext/pg_binary_decoder.c +75 -6
- data/ext/pg_binary_encoder.c +14 -12
- data/ext/pg_coder.c +83 -13
- data/ext/pg_connection.c +627 -351
- data/ext/pg_copy_coder.c +44 -9
- data/ext/pg_result.c +364 -134
- data/ext/pg_text_decoder.c +605 -46
- data/ext/pg_text_encoder.c +95 -76
- data/ext/pg_tuple.c +541 -0
- data/ext/pg_type_map.c +20 -13
- data/ext/pg_type_map_by_column.c +7 -7
- data/ext/pg_type_map_by_mri_type.c +2 -2
- data/ext/pg_type_map_in_ruby.c +4 -7
- data/ext/util.c +7 -7
- data/ext/util.h +3 -3
- data/lib/pg/basic_type_mapping.rb +105 -45
- data/lib/pg/binary_decoder.rb +22 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +109 -39
- data/lib/pg/constants.rb +1 -1
- data/lib/pg/exceptions.rb +1 -1
- data/lib/pg/result.rb +11 -6
- data/lib/pg/text_decoder.rb +25 -20
- data/lib/pg/text_encoder.rb +43 -1
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +1 -1
- data/lib/pg.rb +21 -11
- data/spec/helpers.rb +50 -25
- data/spec/pg/basic_type_mapping_spec.rb +287 -30
- data/spec/pg/connection_spec.rb +695 -282
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +59 -17
- data/spec/pg/tuple_spec.rb +280 -0
- data/spec/pg/type_map_by_class_spec.rb +3 -3
- data/spec/pg/type_map_by_column_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
- data/spec/pg/type_map_by_oid_spec.rb +1 -1
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +319 -35
- data/spec/pg_spec.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +68 -68
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- data/sample/warehouse_partitions.rb +0 -320
data/spec/pg/type_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
require 'pg'
|
5
|
+
require 'time'
|
5
6
|
|
6
7
|
|
7
8
|
describe "PG::Type derivations" do
|
@@ -15,6 +16,11 @@ describe "PG::Type derivations" do
|
|
15
16
|
let!(:textdec_string) { PG::TextDecoder::String.new }
|
16
17
|
let!(:textenc_timestamp) { PG::TextEncoder::TimestampWithoutTimeZone.new }
|
17
18
|
let!(:textdec_timestamp) { PG::TextDecoder::TimestampWithoutTimeZone.new }
|
19
|
+
let!(:textenc_timestamputc) { PG::TextEncoder::TimestampUtc.new }
|
20
|
+
let!(:textdec_timestamputc) { PG::TextDecoder::TimestampUtc.new }
|
21
|
+
let!(:textdec_timestampul) { PG::TextDecoder::TimestampUtcToLocal.new }
|
22
|
+
let!(:textenc_timestamptz) { PG::TextEncoder::TimestampWithTimeZone.new }
|
23
|
+
let!(:textdec_timestamptz) { PG::TextDecoder::TimestampWithTimeZone.new }
|
18
24
|
let!(:textenc_bytea) { PG::TextEncoder::Bytea.new }
|
19
25
|
let!(:textdec_bytea) { PG::TextDecoder::Bytea.new }
|
20
26
|
let!(:binaryenc_int2) { PG::BinaryEncoder::Int2.new }
|
@@ -37,6 +43,14 @@ describe "PG::Type derivations" do
|
|
37
43
|
end.new
|
38
44
|
end
|
39
45
|
|
46
|
+
let!(:intenc_incrementer_with_encoding) do
|
47
|
+
Class.new(PG::SimpleEncoder) do
|
48
|
+
def encode(value, encoding)
|
49
|
+
r = (value.to_i + 1).to_s + " #{encoding}"
|
50
|
+
r.encode!(encoding)
|
51
|
+
end
|
52
|
+
end.new
|
53
|
+
end
|
40
54
|
let!(:intenc_incrementer_with_int_result) do
|
41
55
|
Class.new(PG::SimpleEncoder) do
|
42
56
|
def encode(value)
|
@@ -65,7 +79,7 @@ describe "PG::Type derivations" do
|
|
65
79
|
expect( intdec_incrementer.decode("3") ).to eq( 4 )
|
66
80
|
end
|
67
81
|
|
68
|
-
it "should decode integers of different lengths
|
82
|
+
it "should decode integers of different lengths from text format" do
|
69
83
|
30.times do |zeros|
|
70
84
|
expect( textdec_int.decode("1" + "0"*zeros) ).to eq( 10 ** zeros )
|
71
85
|
expect( textdec_int.decode(zeros==0 ? "0" : "9"*zeros) ).to eq( 10 ** zeros - 1 )
|
@@ -85,6 +99,150 @@ describe "PG::Type derivations" do
|
|
85
99
|
expect( textdec_bytea.decode("\\377\\000") ).to eq( "\xff\0".b )
|
86
100
|
end
|
87
101
|
|
102
|
+
context 'timestamps' do
|
103
|
+
it 'decodes timestamps without timezone as local time' do
|
104
|
+
expect( textdec_timestamp.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
105
|
+
to eq( Time.new(2016,1,2, 23,23,59.123456).iso8601(5) )
|
106
|
+
expect( textdec_timestamp.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
107
|
+
to eq( Time.new(2016,8,2, 23,23,59.123456).iso8601(5) )
|
108
|
+
end
|
109
|
+
it 'decodes timestamps with UTC time and returns UTC timezone' do
|
110
|
+
expect( textdec_timestamputc.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
111
|
+
to eq( Time.utc(2016,1,2, 23,23,59.123456).iso8601(5) )
|
112
|
+
expect( textdec_timestamputc.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
113
|
+
to eq( Time.utc(2016,8,2, 23,23,59.123456).iso8601(5) )
|
114
|
+
end
|
115
|
+
it 'decodes timestamps with UTC time and returns local timezone' do
|
116
|
+
expect( textdec_timestampul.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
117
|
+
to eq( Time.utc(2016,1,2, 23,23,59.123456).getlocal.iso8601(5) )
|
118
|
+
expect( textdec_timestampul.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
119
|
+
to eq( Time.utc(2016,8,2, 23,23,59.123456).getlocal.iso8601(5) )
|
120
|
+
end
|
121
|
+
it 'decodes timestamps with hour timezone' do
|
122
|
+
expect( textdec_timestamptz.decode('2016-01-02 23:23:59.123456-04').iso8601(5) ).
|
123
|
+
to eq( Time.new(2016,1,2, 23,23,59.123456, "-04:00").iso8601(5) )
|
124
|
+
expect( textdec_timestamptz.decode('2016-08-02 23:23:59.123456+10').iso8601(5) ).
|
125
|
+
to eq( Time.new(2016,8,2, 23,23,59.123456, "+10:00").iso8601(5) )
|
126
|
+
expect( textdec_timestamptz.decode('1913-12-31 23:58:59.1231-03').iso8601(5) ).
|
127
|
+
to eq( Time.new(1913, 12, 31, 23, 58, 59.1231, "-03:00").iso8601(5) )
|
128
|
+
expect( textdec_timestamptz.decode('4714-11-24 23:58:59.1231-03 BC').iso8601(5) ).
|
129
|
+
to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231, "-03:00").iso8601(5) )
|
130
|
+
expect( textdec_timestamptz.decode('294276-12-31 23:58:59.1231+03').iso8601(5) ).
|
131
|
+
to eq( Time.new(294276, 12, 31, 23, 58, 59.1231, "+03:00").iso8601(5) )
|
132
|
+
end
|
133
|
+
it 'decodes timestamps with hour:minute timezone' do
|
134
|
+
expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-04:15') ).
|
135
|
+
to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:15") )
|
136
|
+
expect( textdec_timestamptz.decode('2015-07-26 17:26:42.691511-04:30') ).
|
137
|
+
to be_within(0.000001).of( Time.new(2015,07,26, 17, 26, 42.691511, "-04:30") )
|
138
|
+
expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511+10:45') ).
|
139
|
+
to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "+10:45") )
|
140
|
+
end
|
141
|
+
it 'decodes timestamps with hour:minute:sec timezone' do
|
142
|
+
# SET TIME ZONE 'Europe/Dublin'; -- Was UTC−00:25:21 until 1916
|
143
|
+
# SELECT '1900-01-01'::timestamptz;
|
144
|
+
# -- "1900-01-01 00:00:00-00:25:21"
|
145
|
+
expect( textdec_timestamptz.decode('1916-01-01 00:00:00-00:25:21') ).
|
146
|
+
to be_within(0.000001).of( Time.new(1916, 1, 1, 0, 0, 0, "-00:25:21") )
|
147
|
+
end
|
148
|
+
it 'decodes timestamps with date before 1823' do
|
149
|
+
expect( textdec_timestamp.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
150
|
+
to eq( Time.new(1822,01,02, 23, 23, 59.123456).iso8601(5) )
|
151
|
+
expect( textdec_timestamputc.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
152
|
+
to eq( Time.utc(1822,01,02, 23, 23, 59.123456).iso8601(5) )
|
153
|
+
expect( textdec_timestampul.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
154
|
+
to eq( Time.utc(1822,01,02, 23, 23, 59.123456).getlocal.iso8601(5) )
|
155
|
+
expect( textdec_timestamptz.decode('1822-01-02 23:23:59.123456+04').iso8601(5) ).
|
156
|
+
to eq( Time.new(1822,01,02, 23, 23, 59.123456, "+04:00").iso8601(5) )
|
157
|
+
end
|
158
|
+
it 'decodes timestamps with date after 2116' do
|
159
|
+
expect( textdec_timestamp.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
160
|
+
to eq( Time.new(2117,01,02, 23, 23, 59.123456).iso8601(5) )
|
161
|
+
expect( textdec_timestamputc.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
162
|
+
to eq( Time.utc(2117,01,02, 23, 23, 59.123456).iso8601(5) )
|
163
|
+
expect( textdec_timestampul.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
164
|
+
to eq( Time.utc(2117,01,02, 23, 23, 59.123456).getlocal.iso8601(5) )
|
165
|
+
expect( textdec_timestamptz.decode('2117-01-02 23:23:59.123456+04').iso8601(5) ).
|
166
|
+
to eq( Time.new(2117,01,02, 23, 23, 59.123456, "+04:00").iso8601(5) )
|
167
|
+
end
|
168
|
+
it 'decodes timestamps with variable number of digits for the useconds part' do
|
169
|
+
sec = "59.12345678912345"
|
170
|
+
(4..sec.length).each do |i|
|
171
|
+
expect( textdec_timestamp.decode("2016-01-02 23:23:#{sec[0,i]}") ).
|
172
|
+
to be_within(0.000001).of( Time.new(2016,01,02, 23, 23, sec[0,i].to_f) )
|
173
|
+
end
|
174
|
+
end
|
175
|
+
it 'decodes timestamps with leap-second' do
|
176
|
+
expect( textdec_timestamp.decode('1998-12-31 23:59:60.1234') ).
|
177
|
+
to be_within(0.000001).of( Time.new(1998,12,31, 23, 59, 60.1234) )
|
178
|
+
end
|
179
|
+
|
180
|
+
def textdec_timestamptz_decode_should_fail(str)
|
181
|
+
expect(textdec_timestamptz.decode(str)).to eq(str)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'fails when the timestamp is an empty string' do
|
185
|
+
textdec_timestamptz_decode_should_fail('')
|
186
|
+
end
|
187
|
+
it 'fails when the timestamp contains values with less digits than expected' do
|
188
|
+
textdec_timestamptz_decode_should_fail('2016-0-02 23:23:59.123456+00:25:21')
|
189
|
+
textdec_timestamptz_decode_should_fail('2016-01-0 23:23:59.123456+00:25:21')
|
190
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 2:23:59.123456+00:25:21')
|
191
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:2:59.123456+00:25:21')
|
192
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:5.123456+00:25:21')
|
193
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.+00:25:21')
|
194
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+0:25:21')
|
195
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:2:21')
|
196
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:25:2')
|
197
|
+
end
|
198
|
+
it 'fails when the timestamp contains values with more digits than expected' do
|
199
|
+
textdec_timestamptz_decode_should_fail('2016-011-02 23:23:59.123456+00:25:21')
|
200
|
+
textdec_timestamptz_decode_should_fail('2016-01-022 23:23:59.123456+00:25:21')
|
201
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 233:23:59.123456+00:25:21')
|
202
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:233:59.123456+00:25:21')
|
203
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:599.123456+00:25:21')
|
204
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+000:25:21')
|
205
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:255:21')
|
206
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:25:211')
|
207
|
+
end
|
208
|
+
it 'fails when the timestamp contains values with invalid characters' do
|
209
|
+
str = '2013-01-02 23:23:59.123456+00:25:21'
|
210
|
+
str.length.times do |i|
|
211
|
+
textdec_timestamptz_decode_should_fail(str[0,i] + "x" + str[i+1..-1])
|
212
|
+
end
|
213
|
+
end
|
214
|
+
it 'fails when the timestamp contains leading characters' do
|
215
|
+
textdec_timestamptz_decode_should_fail(' 2016-01-02 23:23:59.123456')
|
216
|
+
end
|
217
|
+
it 'fails when the timestamp contains trailing characters' do
|
218
|
+
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456 ')
|
219
|
+
end
|
220
|
+
it 'fails when the timestamp contains non ASCII character' do
|
221
|
+
textdec_timestamptz_decode_should_fail('2016-01ª02 23:23:59.123456')
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context 'identifier quotation' do
|
226
|
+
it 'should build an array out of an quoted identifier string' do
|
227
|
+
quoted_type = PG::TextDecoder::Identifier.new
|
228
|
+
expect( quoted_type.decode(%["A.".".B"]) ).to eq( ["A.", ".B"] )
|
229
|
+
expect( quoted_type.decode(%["'A"".""B'"]) ).to eq( ['\'A"."B\''] )
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'should split unquoted identifier string' do
|
233
|
+
quoted_type = PG::TextDecoder::Identifier.new
|
234
|
+
expect( quoted_type.decode(%[a.b]) ).to eq( ['a','b'] )
|
235
|
+
expect( quoted_type.decode(%[a]) ).to eq( ['a'] )
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should split identifier string with correct character encoding' do
|
239
|
+
quoted_type = PG::TextDecoder::Identifier.new
|
240
|
+
v = quoted_type.decode(%[Héllo].encode("iso-8859-1")).first
|
241
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
242
|
+
expect( v ).to eq( %[Héllo].encode(Encoding::ISO_8859_1) )
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
88
246
|
it "should raise when decode method is called with wrong args" do
|
89
247
|
expect{ textdec_int.decode() }.to raise_error(ArgumentError)
|
90
248
|
expect{ textdec_int.decode("123", 2, 3, 4) }.to raise_error(ArgumentError)
|
@@ -156,10 +314,60 @@ describe "PG::Type derivations" do
|
|
156
314
|
expect( textenc_bytea.encode("\x00\x01\x02\x03\xef".b) ).to eq( "\\x00010203ef" )
|
157
315
|
end
|
158
316
|
|
317
|
+
context 'timestamps' do
|
318
|
+
it 'encodes timestamps without timezone' do
|
319
|
+
expect( textenc_timestamp.encode(Time.new(2016,1,2, 23, 23, 59.123456, 3*60*60)) ).
|
320
|
+
to match( /^2016-01-02 23:23:59.12345\d+$/ )
|
321
|
+
expect( textenc_timestamp.encode(Time.new(2016,8,2, 23, 23, 59.123456, 3*60*60)) ).
|
322
|
+
to match( /^2016-08-02 23:23:59.12345\d+$/ )
|
323
|
+
end
|
324
|
+
it 'encodes timestamps with UTC timezone' do
|
325
|
+
expect( textenc_timestamputc.encode(Time.new(2016,1,2, 23, 23, 59.123456, 3*60*60)) ).
|
326
|
+
to match( /^2016-01-02 20:23:59.12345\d+$/ )
|
327
|
+
expect( textenc_timestamputc.encode(Time.new(2016,8,2, 23, 23, 59.123456, 3*60*60)) ).
|
328
|
+
to match( /^2016-08-02 20:23:59.12345\d+$/ )
|
329
|
+
end
|
330
|
+
it 'encodes timestamps with hour timezone' do
|
331
|
+
expect( textenc_timestamptz.encode(Time.new(2016,1,02, 23, 23, 59.123456, -4*60*60)) ).
|
332
|
+
to match( /^2016-01-02 23:23:59.12345\d+ \-04:00$/ )
|
333
|
+
expect( textenc_timestamptz.encode(Time.new(2016,8,02, 23, 23, 59.123456, 10*60*60)) ).
|
334
|
+
to match( /^2016-08-02 23:23:59.12345\d+ \+10:00$/ )
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context 'identifier quotation' do
|
339
|
+
it 'should quote and escape identifier' do
|
340
|
+
quoted_type = PG::TextEncoder::Identifier.new
|
341
|
+
expect( quoted_type.encode(['schema','table','col']) ).to eq( %["schema"."table"."col"] )
|
342
|
+
expect( quoted_type.encode(['A.','.B']) ).to eq( %["A.".".B"] )
|
343
|
+
expect( quoted_type.encode(%['A"."B']) ).to eq( %["'A"".""B'"] )
|
344
|
+
expect( quoted_type.encode( nil ) ).to be_nil
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'should quote identifiers with correct character encoding' do
|
348
|
+
quoted_type = PG::TextEncoder::Identifier.new
|
349
|
+
v = quoted_type.encode(['Héllo'], "iso-8859-1")
|
350
|
+
expect( v ).to eq( %["Héllo"].encode(Encoding::ISO_8859_1) )
|
351
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
352
|
+
end
|
353
|
+
|
354
|
+
it "will raise a TypeError for invalid arguments to quote_ident" do
|
355
|
+
quoted_type = PG::TextEncoder::Identifier.new
|
356
|
+
expect{ quoted_type.encode( [nil] ) }.to raise_error(TypeError)
|
357
|
+
expect{ quoted_type.encode( [['a']] ) }.to raise_error(TypeError)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
159
361
|
it "should encode with ruby encoder" do
|
160
362
|
expect( intenc_incrementer.encode(3) ).to eq( "4 " )
|
161
363
|
end
|
162
364
|
|
365
|
+
it "should encode with ruby encoder and given character encoding" do
|
366
|
+
r = intenc_incrementer_with_encoding.encode(3, Encoding::CP850)
|
367
|
+
expect( r ).to eq( "4 CP850" )
|
368
|
+
expect( r.encoding ).to eq( Encoding::CP850 )
|
369
|
+
end
|
370
|
+
|
163
371
|
it "should return when ruby encoder returns non string values" do
|
164
372
|
expect( intenc_incrementer_with_int_result.encode(3) ).to eq( 4 )
|
165
373
|
end
|
@@ -215,6 +423,7 @@ describe "PG::Type derivations" do
|
|
215
423
|
describe "Array types" do
|
216
424
|
let!(:textenc_string_array) { PG::TextEncoder::Array.new elements_type: textenc_string }
|
217
425
|
let!(:textdec_string_array) { PG::TextDecoder::Array.new elements_type: textdec_string }
|
426
|
+
let!(:textdec_string_array_raise) { PG::TextDecoder::Array.new elements_type: textdec_string, flags: PG::Coder:: FORMAT_ERROR_TO_RAISE }
|
218
427
|
let!(:textenc_int_array) { PG::TextEncoder::Array.new elements_type: textenc_int, needs_quotation: false }
|
219
428
|
let!(:textdec_int_array) { PG::TextDecoder::Array.new elements_type: textdec_int, needs_quotation: false }
|
220
429
|
let!(:textenc_float_array) { PG::TextEncoder::Array.new elements_type: textenc_float, needs_quotation: false }
|
@@ -280,6 +489,57 @@ describe "PG::Type derivations" do
|
|
280
489
|
it 'respects a different delimiter' do
|
281
490
|
expect( textdec_string_array_with_delimiter.decode(%[{1;2;3}]) ).to eq( ['1','2','3'] )
|
282
491
|
end
|
492
|
+
|
493
|
+
it 'ignores array dimensions' do
|
494
|
+
expect( textdec_string_array.decode(%[[2:4]={1,2,3}]) ).to eq( ['1','2','3'] )
|
495
|
+
expect( textdec_string_array.decode(%[[]={1,2,3}]) ).to eq( ['1','2','3'] )
|
496
|
+
expect( textdec_string_array.decode(%[ [-1:+2]= {4,3,2,1}]) ).to eq( ['4','3','2','1'] )
|
497
|
+
end
|
498
|
+
|
499
|
+
it 'ignores spaces after array' do
|
500
|
+
expect( textdec_string_array.decode(%[[2:4]={1,2,3} ]) ).to eq( ['1','2','3'] )
|
501
|
+
expect( textdec_string_array.decode(%[{1,2,3} ]) ).to eq( ['1','2','3'] )
|
502
|
+
end
|
503
|
+
|
504
|
+
describe "with malformed syntax are deprecated" do
|
505
|
+
it 'accepts broken array dimensions' do
|
506
|
+
expect( textdec_string_array.decode(%([2:4={1,2,3})) ).to eq([['1','2','3']])
|
507
|
+
expect( textdec_string_array.decode(%(2:4]={1,2,3})) ).to eq([['1','2','3']])
|
508
|
+
expect( textdec_string_array.decode(%(={1,2,3})) ).to eq([['1','2','3']])
|
509
|
+
expect( textdec_string_array.decode(%([x]={1,2,3})) ).to eq([['1','2','3']])
|
510
|
+
expect( textdec_string_array.decode(%([]{1,2,3})) ).to eq([['1','2','3']])
|
511
|
+
expect( textdec_string_array.decode(%(1,2,3)) ).to eq(['','2'])
|
512
|
+
end
|
513
|
+
|
514
|
+
it 'accepts malformed arrays' do
|
515
|
+
expect( textdec_string_array.decode(%({1,2,3)) ).to eq(['1','2'])
|
516
|
+
expect( textdec_string_array.decode(%({1,2,3}})) ).to eq(['1','2','3'])
|
517
|
+
expect( textdec_string_array.decode(%({1,2,3}x)) ).to eq(['1','2','3'])
|
518
|
+
expect( textdec_string_array.decode(%({{1,2},{2,3})) ).to eq([['1','2'],['2','3']])
|
519
|
+
expect( textdec_string_array.decode(%({{1,2},{2,3}}x)) ).to eq([['1','2'],['2','3']])
|
520
|
+
expect( textdec_string_array.decode(%({[1,2},{2,3}}})) ).to eq(['[1','2'])
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
describe "with malformed syntax are raised with pg-2.0+" do
|
525
|
+
it 'complains about broken array dimensions' do
|
526
|
+
expect{ textdec_string_array_raise.decode(%([2:4={1,2,3})) }.to raise_error(TypeError)
|
527
|
+
expect{ textdec_string_array_raise.decode(%(2:4]={1,2,3})) }.to raise_error(TypeError)
|
528
|
+
expect{ textdec_string_array_raise.decode(%(={1,2,3})) }.to raise_error(TypeError)
|
529
|
+
expect{ textdec_string_array_raise.decode(%([x]={1,2,3})) }.to raise_error(TypeError)
|
530
|
+
expect{ textdec_string_array_raise.decode(%([]{1,2,3})) }.to raise_error(TypeError)
|
531
|
+
expect{ textdec_string_array_raise.decode(%(1,2,3)) }.to raise_error(TypeError)
|
532
|
+
end
|
533
|
+
|
534
|
+
it 'complains about malformed array' do
|
535
|
+
expect{ textdec_string_array_raise.decode(%({1,2,3)) }.to raise_error(TypeError)
|
536
|
+
expect{ textdec_string_array_raise.decode(%({1,2,3}})) }.to raise_error(TypeError)
|
537
|
+
expect{ textdec_string_array_raise.decode(%({1,2,3}x)) }.to raise_error(TypeError)
|
538
|
+
expect{ textdec_string_array_raise.decode(%({{1,2},{2,3})) }.to raise_error(TypeError)
|
539
|
+
expect{ textdec_string_array_raise.decode(%({{1,2},{2,3}}x)) }.to raise_error(TypeError)
|
540
|
+
expect{ textdec_string_array_raise.decode(%({[1,2},{2,3}}})) }.to raise_error(TypeError)
|
541
|
+
end
|
542
|
+
end
|
283
543
|
end
|
284
544
|
|
285
545
|
context 'bytea' do
|
@@ -347,20 +607,6 @@ describe "PG::Type derivations" do
|
|
347
607
|
array_type = PG::TextDecoder::Array.new elements_type: nil
|
348
608
|
expect( array_type.decode(%[{3,4}]) ).to eq( ['3','4'] )
|
349
609
|
end
|
350
|
-
|
351
|
-
context 'identifier quotation' do
|
352
|
-
it 'should build an array out of an quoted identifier string' do
|
353
|
-
quoted_type = PG::TextDecoder::Identifier.new elements_type: textdec_string
|
354
|
-
expect( quoted_type.decode(%["A.".".B"]) ).to eq( ["A.", ".B"] )
|
355
|
-
expect( quoted_type.decode(%["'A"".""B'"]) ).to eq( ['\'A"."B\''] )
|
356
|
-
end
|
357
|
-
|
358
|
-
it 'should split unquoted identifier string' do
|
359
|
-
quoted_type = PG::TextDecoder::Identifier.new elements_type: textdec_string
|
360
|
-
expect( quoted_type.decode(%[a.b]) ).to eq( ['a','b'] )
|
361
|
-
expect( quoted_type.decode(%[a]) ).to eq( ['a'] )
|
362
|
-
end
|
363
|
-
end
|
364
610
|
end
|
365
611
|
|
366
612
|
describe '#encode' do
|
@@ -401,9 +647,18 @@ describe "PG::Type derivations" do
|
|
401
647
|
end
|
402
648
|
|
403
649
|
context 'array of types with encoder in ruby space' do
|
404
|
-
it 'encodes with quotation' do
|
650
|
+
it 'encodes with quotation and default character encoding' do
|
651
|
+
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
|
652
|
+
r = array_type.encode([3,4])
|
653
|
+
expect( r ).to eq( %[{"4 ","5 "}] )
|
654
|
+
expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
|
655
|
+
end
|
656
|
+
|
657
|
+
it 'encodes with quotation and given character encoding' do
|
405
658
|
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
|
406
|
-
|
659
|
+
r = array_type.encode([3,4], Encoding::CP850)
|
660
|
+
expect( r ).to eq( %[{"4 ","5 "}] )
|
661
|
+
expect( r.encoding ).to eq( Encoding::CP850 )
|
407
662
|
end
|
408
663
|
|
409
664
|
it 'encodes without quotation' do
|
@@ -411,6 +666,20 @@ describe "PG::Type derivations" do
|
|
411
666
|
expect( array_type.encode([3,4]) ).to eq( %[{4 ,5 }] )
|
412
667
|
end
|
413
668
|
|
669
|
+
it 'encodes with default character encoding' do
|
670
|
+
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
|
671
|
+
r = array_type.encode([3,4])
|
672
|
+
expect( r ).to eq( %[{"4 ASCII-8BIT","5 ASCII-8BIT"}] )
|
673
|
+
expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
|
674
|
+
end
|
675
|
+
|
676
|
+
it 'encodes with given character encoding' do
|
677
|
+
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
|
678
|
+
r = array_type.encode([3,4], Encoding::CP850)
|
679
|
+
expect( r ).to eq( %[{"4 CP850","5 CP850"}] )
|
680
|
+
expect( r.encoding ).to eq( Encoding::CP850 )
|
681
|
+
end
|
682
|
+
|
414
683
|
it "should raise when ruby encoder returns non string values" do
|
415
684
|
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_int_result, needs_quotation: false
|
416
685
|
expect{ array_type.encode([3,4]) }.to raise_error(TypeError)
|
@@ -422,27 +691,18 @@ describe "PG::Type derivations" do
|
|
422
691
|
expect( textenc_float_array.encode(1234) ).to eq( "1234" )
|
423
692
|
end
|
424
693
|
|
425
|
-
context 'identifier quotation' do
|
426
|
-
it 'should quote and escape identifier' do
|
427
|
-
quoted_type = PG::TextEncoder::Identifier.new elements_type: textenc_string
|
428
|
-
expect( quoted_type.encode(['schema','table','col']) ).to eq( %["schema"."table"."col"] )
|
429
|
-
expect( quoted_type.encode(['A.','.B']) ).to eq( %["A.".".B"] )
|
430
|
-
expect( quoted_type.encode(%['A"."B']) ).to eq( %["'A"".""B'"] )
|
431
|
-
end
|
432
|
-
|
433
|
-
it 'shouldn\'t quote or escape identifier if requested to not do' do
|
434
|
-
quoted_type = PG::TextEncoder::Identifier.new elements_type: textenc_string,
|
435
|
-
needs_quotation: false
|
436
|
-
expect( quoted_type.encode(['a','b']) ).to eq( %[a.b] )
|
437
|
-
expect( quoted_type.encode(%[a.b]) ).to eq( %[a.b] )
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
694
|
context 'literal quotation' do
|
442
695
|
it 'should quote and escape literals' do
|
443
696
|
quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
|
444
697
|
expect( quoted_type.encode(["'A\",","\\B'"]) ).to eq( %['{"''A\\",","\\\\B''"}'] )
|
445
698
|
end
|
699
|
+
|
700
|
+
it 'should quote literals with correct character encoding' do
|
701
|
+
quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
|
702
|
+
v = quoted_type.encode(["Héllo"], "iso-8859-1")
|
703
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
704
|
+
expect( v ).to eq( %['{Héllo}'].encode(Encoding::ISO_8859_1) )
|
705
|
+
end
|
446
706
|
end
|
447
707
|
end
|
448
708
|
|
@@ -489,11 +749,22 @@ describe "PG::Type derivations" do
|
|
489
749
|
expect( e.encode("xxxx") ).to eq("eHh4eA==")
|
490
750
|
expect( e.encode("xxxxx") ).to eq("eHh4eHg=")
|
491
751
|
expect( e.encode("\0\n\t") ).to eq("AAoJ")
|
752
|
+
expect( e.encode("(\xFBm") ).to eq("KPtt")
|
753
|
+
end
|
754
|
+
|
755
|
+
it 'should encode Strings as base64 with correct character encoding' do
|
756
|
+
e = PG::TextEncoder::ToBase64.new
|
757
|
+
v = e.encode("Héllo".encode("utf-16le"), "iso-8859-1")
|
758
|
+
expect( v ).to eq("SOlsbG8=")
|
759
|
+
expect( v.encoding ).to eq(Encoding::ISO_8859_1)
|
492
760
|
end
|
493
761
|
|
494
762
|
it "should encode Strings as base64 in BinaryDecoder" do
|
495
763
|
e = PG::BinaryDecoder::ToBase64.new
|
496
764
|
expect( e.decode("x") ).to eq("eA==")
|
765
|
+
v = e.decode("Héllo".encode("utf-16le"))
|
766
|
+
expect( v ).to eq("SADpAGwAbABvAA==")
|
767
|
+
expect( v.encoding ).to eq(Encoding::ASCII_8BIT)
|
497
768
|
end
|
498
769
|
|
499
770
|
it "should encode Integers as base64" do
|
@@ -517,6 +788,7 @@ describe "PG::Type derivations" do
|
|
517
788
|
expect( e.decode("eHh4eA==") ).to eq("xxxx")
|
518
789
|
expect( e.decode("eHh4eHg=") ).to eq("xxxxx")
|
519
790
|
expect( e.decode("AAoJ") ).to eq("\0\n\t")
|
791
|
+
expect( e.decode("KPtt") ).to eq("(\xFBm")
|
520
792
|
end
|
521
793
|
|
522
794
|
it "should decode base64 in BinaryEncoder" do
|
@@ -579,6 +851,12 @@ describe "PG::Type derivations" do
|
|
579
851
|
expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
|
580
852
|
to eq("xyz\t123\t2456\t34567\t456789\t5678901\t[1, 2, 3]\t12.1\tabcdefg\t\\N\n")
|
581
853
|
end
|
854
|
+
|
855
|
+
it 'should output a string with correct character encoding' do
|
856
|
+
v = encoder.encode(["Héllo"], "iso-8859-1")
|
857
|
+
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
858
|
+
expect( v ).to eq( "Héllo\n".encode(Encoding::ISO_8859_1) )
|
859
|
+
end
|
582
860
|
end
|
583
861
|
|
584
862
|
context "with TypeMapByClass" do
|
@@ -640,9 +918,15 @@ describe "PG::Type derivations" do
|
|
640
918
|
end
|
641
919
|
|
642
920
|
describe '#decode' do
|
643
|
-
it "should decode
|
921
|
+
it "should decode COPY text format to array of strings" do
|
644
922
|
expect( decoder.decode("123\t \0#\t#\n#\r#\\ \t234\t#\x01#\002\n".gsub("#", "\\"))).to eq( ["123", " \0\t\n\r\\ ", "234", "\x01\x02"] )
|
645
923
|
end
|
924
|
+
|
925
|
+
it 'should respect input character encoding' do
|
926
|
+
v = decoder.decode("Héllo\n".encode("iso-8859-1")).first
|
927
|
+
expect( v.encoding ).to eq(Encoding::ISO_8859_1)
|
928
|
+
expect( v ).to eq("Héllo".encode("iso-8859-1"))
|
929
|
+
end
|
646
930
|
end
|
647
931
|
end
|
648
932
|
|
data/spec/pg_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
require_relative 'helpers'
|
@@ -7,7 +7,7 @@ require 'pg'
|
|
7
7
|
|
8
8
|
describe PG do
|
9
9
|
|
10
|
-
it "knows what version of the libpq library is loaded"
|
10
|
+
it "knows what version of the libpq library is loaded" do
|
11
11
|
expect( PG.library_version ).to be_an( Integer )
|
12
12
|
expect( PG.library_version ).to be >= 90100
|
13
13
|
end
|
data.tar.gz.sig
CHANGED
Binary file
|