pg 0.18.0 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|