pg 1.2.3 → 1.3.1
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.appveyor.yml +36 -0
- data/.gems +6 -0
- data/.github/workflows/binary-gems.yml +86 -0
- data/.github/workflows/source-gem.yml +130 -0
- data/.gitignore +13 -0
- data/.hgsigs +34 -0
- data/.hgtags +41 -0
- data/.irbrc +23 -0
- data/.pryrc +23 -0
- data/.tm_properties +21 -0
- data/.travis.yml +49 -0
- data/Gemfile +14 -0
- data/History.rdoc +95 -7
- data/Manifest.txt +0 -1
- data/README.rdoc +7 -6
- data/Rakefile +27 -138
- data/Rakefile.cross +6 -5
- data/certs/ged.pem +24 -0
- data/ext/errorcodes.def +8 -0
- data/ext/errorcodes.txt +3 -1
- data/ext/extconf.rb +91 -19
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -0
- data/ext/pg.c +59 -4
- data/ext/pg.h +18 -1
- data/ext/pg_coder.c +82 -28
- data/ext/pg_connection.c +673 -493
- data/ext/pg_copy_coder.c +45 -16
- data/ext/pg_record_coder.c +39 -11
- data/ext/pg_result.c +61 -31
- data/ext/pg_text_decoder.c +1 -1
- data/ext/pg_text_encoder.c +6 -6
- data/ext/pg_tuple.c +47 -21
- data/ext/pg_type_map.c +41 -8
- data/ext/pg_type_map_all_strings.c +14 -1
- data/ext/pg_type_map_by_class.c +49 -24
- data/ext/pg_type_map_by_column.c +64 -28
- data/ext/pg_type_map_by_mri_type.c +47 -18
- data/ext/pg_type_map_by_oid.c +52 -23
- data/ext/pg_type_map_in_ruby.c +50 -19
- data/ext/pg_util.c +2 -2
- data/lib/pg/basic_type_map_based_on_result.rb +47 -0
- data/lib/pg/basic_type_map_for_queries.rb +193 -0
- data/lib/pg/basic_type_map_for_results.rb +81 -0
- data/lib/pg/basic_type_registry.rb +296 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +588 -58
- data/lib/pg/version.rb +4 -0
- data/lib/pg.rb +47 -32
- data/misc/openssl-pg-segfault.rb +31 -0
- data/misc/postgres/History.txt +9 -0
- data/misc/postgres/Manifest.txt +5 -0
- data/misc/postgres/README.txt +21 -0
- data/misc/postgres/Rakefile +21 -0
- data/misc/postgres/lib/postgres.rb +16 -0
- data/misc/ruby-pg/History.txt +9 -0
- data/misc/ruby-pg/Manifest.txt +5 -0
- data/misc/ruby-pg/README.txt +21 -0
- data/misc/ruby-pg/Rakefile +21 -0
- data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
- data/pg.gemspec +32 -0
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +106 -0
- data/sample/async_copyto.rb +39 -0
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copydata.rb +71 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +19 -0
- data/sample/cursor.rb +21 -0
- data/sample/disk_usage_report.rb +177 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +69 -0
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +72 -0
- data/sample/pg_statistics.rb +285 -0
- data/sample/replication_monitor.rb +222 -0
- data/sample/test_binary_values.rb +33 -0
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +311 -0
- data.tar.gz.sig +0 -0
- metadata +76 -217
- metadata.gz.sig +0 -0
- data/ChangeLog +0 -0
- data/lib/pg/basic_type_mapping.rb +0 -522
- data/spec/data/expected_trace.out +0 -26
- data/spec/data/random_binary_data +0 -0
- data/spec/helpers.rb +0 -380
- data/spec/pg/basic_type_mapping_spec.rb +0 -630
- data/spec/pg/connection_spec.rb +0 -1949
- data/spec/pg/connection_sync_spec.rb +0 -41
- data/spec/pg/result_spec.rb +0 -681
- data/spec/pg/tuple_spec.rb +0 -333
- data/spec/pg/type_map_by_class_spec.rb +0 -138
- data/spec/pg/type_map_by_column_spec.rb +0 -226
- data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
- data/spec/pg/type_map_by_oid_spec.rb +0 -149
- data/spec/pg/type_map_in_ruby_spec.rb +0 -164
- data/spec/pg/type_map_spec.rb +0 -22
- data/spec/pg/type_spec.rb +0 -1123
- data/spec/pg_spec.rb +0 -50
data/spec/pg/type_spec.rb
DELETED
@@ -1,1123 +0,0 @@
|
|
1
|
-
# -*- rspec -*-
|
2
|
-
# encoding: utf-8
|
3
|
-
|
4
|
-
require 'pg'
|
5
|
-
require 'time'
|
6
|
-
|
7
|
-
|
8
|
-
describe "PG::Type derivations" do
|
9
|
-
let!(:textenc_int) { PG::TextEncoder::Integer.new name: 'Integer', oid: 23 }
|
10
|
-
let!(:textdec_int) { PG::TextDecoder::Integer.new name: 'Integer', oid: 23 }
|
11
|
-
let!(:textenc_boolean) { PG::TextEncoder::Boolean.new }
|
12
|
-
let!(:textdec_boolean) { PG::TextDecoder::Boolean.new }
|
13
|
-
let!(:textenc_float) { PG::TextEncoder::Float.new }
|
14
|
-
let!(:textdec_float) { PG::TextDecoder::Float.new }
|
15
|
-
let!(:textenc_numeric) { PG::TextEncoder::Numeric.new }
|
16
|
-
let!(:textenc_string) { PG::TextEncoder::String.new }
|
17
|
-
let!(:textdec_string) { PG::TextDecoder::String.new }
|
18
|
-
let!(:textenc_timestamp) { PG::TextEncoder::TimestampWithoutTimeZone.new }
|
19
|
-
let!(:textdec_timestamp) { PG::TextDecoder::TimestampWithoutTimeZone.new }
|
20
|
-
let!(:textenc_timestamputc) { PG::TextEncoder::TimestampUtc.new }
|
21
|
-
let!(:textdec_timestamputc) { PG::TextDecoder::TimestampUtc.new }
|
22
|
-
let!(:textdec_timestampul) { PG::TextDecoder::TimestampUtcToLocal.new }
|
23
|
-
let!(:textenc_timestamptz) { PG::TextEncoder::TimestampWithTimeZone.new }
|
24
|
-
let!(:textdec_timestamptz) { PG::TextDecoder::TimestampWithTimeZone.new }
|
25
|
-
let!(:textenc_bytea) { PG::TextEncoder::Bytea.new }
|
26
|
-
let!(:textdec_bytea) { PG::TextDecoder::Bytea.new }
|
27
|
-
let!(:binaryenc_int2) { PG::BinaryEncoder::Int2.new }
|
28
|
-
let!(:binaryenc_int4) { PG::BinaryEncoder::Int4.new }
|
29
|
-
let!(:binaryenc_int8) { PG::BinaryEncoder::Int8.new }
|
30
|
-
let!(:binarydec_integer) { PG::BinaryDecoder::Integer.new }
|
31
|
-
|
32
|
-
let!(:intenc_incrementer) do
|
33
|
-
Class.new(PG::SimpleEncoder) do
|
34
|
-
def encode(value)
|
35
|
-
(value.to_i + 1).to_s + " "
|
36
|
-
end
|
37
|
-
end.new
|
38
|
-
end
|
39
|
-
let!(:intdec_incrementer) do
|
40
|
-
Class.new(PG::SimpleDecoder) do
|
41
|
-
def decode(string, tuple=nil, field=nil)
|
42
|
-
string.to_i+1
|
43
|
-
end
|
44
|
-
end.new
|
45
|
-
end
|
46
|
-
|
47
|
-
let!(:intenc_incrementer_with_encoding) do
|
48
|
-
Class.new(PG::SimpleEncoder) do
|
49
|
-
def encode(value, encoding)
|
50
|
-
r = (value.to_i + 1).to_s + " #{encoding}"
|
51
|
-
r.encode!(encoding)
|
52
|
-
end
|
53
|
-
end.new
|
54
|
-
end
|
55
|
-
let!(:intenc_incrementer_with_int_result) do
|
56
|
-
Class.new(PG::SimpleEncoder) do
|
57
|
-
def encode(value)
|
58
|
-
value.to_i+1
|
59
|
-
end
|
60
|
-
end.new
|
61
|
-
end
|
62
|
-
|
63
|
-
it "shouldn't be possible to build a PG::Type directly" do
|
64
|
-
expect{ PG::Coder.new }.to raise_error(TypeError, /cannot/)
|
65
|
-
end
|
66
|
-
|
67
|
-
describe PG::SimpleCoder do
|
68
|
-
describe '#decode' do
|
69
|
-
it "should offer decode method with tuple/field" do
|
70
|
-
res = textdec_int.decode("123", 1, 1)
|
71
|
-
expect( res ).to eq( 123 )
|
72
|
-
end
|
73
|
-
|
74
|
-
it "should offer decode method without tuple/field" do
|
75
|
-
res = textdec_int.decode("234")
|
76
|
-
expect( res ).to eq( 234 )
|
77
|
-
end
|
78
|
-
|
79
|
-
it "should decode with ruby decoder" do
|
80
|
-
expect( intdec_incrementer.decode("3") ).to eq( 4 )
|
81
|
-
end
|
82
|
-
|
83
|
-
it "should decode integers of different lengths from text format" do
|
84
|
-
30.times do |zeros|
|
85
|
-
expect( textdec_int.decode("1" + "0"*zeros) ).to eq( 10 ** zeros )
|
86
|
-
expect( textdec_int.decode(zeros==0 ? "0" : "9"*zeros) ).to eq( 10 ** zeros - 1 )
|
87
|
-
expect( textdec_int.decode("-1" + "0"*zeros) ).to eq( -10 ** zeros )
|
88
|
-
expect( textdec_int.decode(zeros==0 ? "0" : "-" + "9"*zeros) ).to eq( -10 ** zeros + 1 )
|
89
|
-
end
|
90
|
-
66.times do |bits|
|
91
|
-
expect( textdec_int.decode((2 ** bits).to_s) ).to eq( 2 ** bits )
|
92
|
-
expect( textdec_int.decode((2 ** bits - 1).to_s) ).to eq( 2 ** bits - 1 )
|
93
|
-
expect( textdec_int.decode((-2 ** bits).to_s) ).to eq( -2 ** bits )
|
94
|
-
expect( textdec_int.decode((-2 ** bits + 1).to_s) ).to eq( -2 ** bits + 1 )
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'decodes bytea to a binary string' do
|
99
|
-
expect( textdec_bytea.decode("\\x00010203EF") ).to eq( "\x00\x01\x02\x03\xef".b )
|
100
|
-
expect( textdec_bytea.decode("\\377\\000") ).to eq( "\xff\0".b )
|
101
|
-
end
|
102
|
-
|
103
|
-
context 'timestamps' do
|
104
|
-
it 'decodes timestamps without timezone as local time' do
|
105
|
-
expect( textdec_timestamp.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
106
|
-
to eq( Time.new(2016,1,2, 23,23,59.123456).iso8601(5) )
|
107
|
-
expect( textdec_timestamp.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
108
|
-
to eq( Time.new(2016,8,2, 23,23,59.123456).iso8601(5) )
|
109
|
-
end
|
110
|
-
it 'decodes timestamps with UTC time and returns UTC timezone' do
|
111
|
-
expect( textdec_timestamputc.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
112
|
-
to eq( Time.utc(2016,1,2, 23,23,59.123456).iso8601(5) )
|
113
|
-
expect( textdec_timestamputc.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
114
|
-
to eq( Time.utc(2016,8,2, 23,23,59.123456).iso8601(5) )
|
115
|
-
end
|
116
|
-
it 'decodes timestamps with UTC time and returns local timezone' do
|
117
|
-
expect( textdec_timestampul.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
|
118
|
-
to eq( Time.utc(2016,1,2, 23,23,59.123456).getlocal.iso8601(5) )
|
119
|
-
expect( textdec_timestampul.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
|
120
|
-
to eq( Time.utc(2016,8,2, 23,23,59.123456).getlocal.iso8601(5) )
|
121
|
-
end
|
122
|
-
it 'decodes timestamps with hour timezone' do
|
123
|
-
expect( textdec_timestamptz.decode('2016-01-02 23:23:59.123456-04').iso8601(5) ).
|
124
|
-
to eq( Time.new(2016,1,2, 23,23,59.123456, "-04:00").iso8601(5) )
|
125
|
-
expect( textdec_timestamptz.decode('2016-08-02 23:23:59.123456+10').iso8601(5) ).
|
126
|
-
to eq( Time.new(2016,8,2, 23,23,59.123456, "+10:00").iso8601(5) )
|
127
|
-
expect( textdec_timestamptz.decode('1913-12-31 23:58:59.1231-03').iso8601(5) ).
|
128
|
-
to eq( Time.new(1913, 12, 31, 23, 58, 59.1231, "-03:00").iso8601(5) )
|
129
|
-
expect( textdec_timestamptz.decode('4714-11-24 23:58:59.1231-03 BC').iso8601(5) ).
|
130
|
-
to eq( Time.new(-4713, 11, 24, 23, 58, 59.1231, "-03:00").iso8601(5) )
|
131
|
-
expect( textdec_timestamptz.decode('294276-12-31 23:58:59.1231+03').iso8601(5) ).
|
132
|
-
to eq( Time.new(294276, 12, 31, 23, 58, 59.1231, "+03:00").iso8601(5) )
|
133
|
-
end
|
134
|
-
it 'decodes timestamps with hour:minute timezone' do
|
135
|
-
expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-04:15') ).
|
136
|
-
to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:15") )
|
137
|
-
expect( textdec_timestamptz.decode('2015-07-26 17:26:42.691511-04:30') ).
|
138
|
-
to be_within(0.000001).of( Time.new(2015,07,26, 17, 26, 42.691511, "-04:30") )
|
139
|
-
expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511+10:45') ).
|
140
|
-
to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "+10:45") )
|
141
|
-
end
|
142
|
-
it 'decodes timestamps with hour:minute:sec timezone' do
|
143
|
-
# SET TIME ZONE 'Europe/Dublin'; -- Was UTC−00:25:21 until 1916
|
144
|
-
# SELECT '1900-01-01'::timestamptz;
|
145
|
-
# -- "1900-01-01 00:00:00-00:25:21"
|
146
|
-
expect( textdec_timestamptz.decode('1916-01-01 00:00:00-00:25:21') ).
|
147
|
-
to be_within(0.000001).of( Time.new(1916, 1, 1, 0, 0, 0, "-00:25:21") )
|
148
|
-
end
|
149
|
-
it 'decodes timestamps with date before 1823' do
|
150
|
-
expect( textdec_timestamp.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
151
|
-
to eq( Time.new(1822,01,02, 23, 23, 59.123456).iso8601(5) )
|
152
|
-
expect( textdec_timestamputc.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
153
|
-
to eq( Time.utc(1822,01,02, 23, 23, 59.123456).iso8601(5) )
|
154
|
-
expect( textdec_timestampul.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
|
155
|
-
to eq( Time.utc(1822,01,02, 23, 23, 59.123456).getlocal.iso8601(5) )
|
156
|
-
expect( textdec_timestamptz.decode('1822-01-02 23:23:59.123456+04').iso8601(5) ).
|
157
|
-
to eq( Time.new(1822,01,02, 23, 23, 59.123456, "+04:00").iso8601(5) )
|
158
|
-
end
|
159
|
-
it 'decodes timestamps with date after 2116' do
|
160
|
-
expect( textdec_timestamp.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
161
|
-
to eq( Time.new(2117,01,02, 23, 23, 59.123456).iso8601(5) )
|
162
|
-
expect( textdec_timestamputc.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
163
|
-
to eq( Time.utc(2117,01,02, 23, 23, 59.123456).iso8601(5) )
|
164
|
-
expect( textdec_timestampul.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
|
165
|
-
to eq( Time.utc(2117,01,02, 23, 23, 59.123456).getlocal.iso8601(5) )
|
166
|
-
expect( textdec_timestamptz.decode('2117-01-02 23:23:59.123456+04').iso8601(5) ).
|
167
|
-
to eq( Time.new(2117,01,02, 23, 23, 59.123456, "+04:00").iso8601(5) )
|
168
|
-
end
|
169
|
-
it 'decodes timestamps with variable number of digits for the useconds part' do
|
170
|
-
sec = "59.12345678912345"
|
171
|
-
(4..sec.length).each do |i|
|
172
|
-
expect( textdec_timestamp.decode("2016-01-02 23:23:#{sec[0,i]}") ).
|
173
|
-
to be_within(0.000001).of( Time.new(2016,01,02, 23, 23, sec[0,i].to_f) )
|
174
|
-
end
|
175
|
-
end
|
176
|
-
it 'decodes timestamps with leap-second' do
|
177
|
-
expect( textdec_timestamp.decode('1998-12-31 23:59:60.1234') ).
|
178
|
-
to be_within(0.000001).of( Time.new(1998,12,31, 23, 59, 60.1234) )
|
179
|
-
end
|
180
|
-
|
181
|
-
def textdec_timestamptz_decode_should_fail(str)
|
182
|
-
expect(textdec_timestamptz.decode(str)).to eq(str)
|
183
|
-
end
|
184
|
-
|
185
|
-
it 'fails when the timestamp is an empty string' do
|
186
|
-
textdec_timestamptz_decode_should_fail('')
|
187
|
-
end
|
188
|
-
it 'fails when the timestamp contains values with less digits than expected' do
|
189
|
-
textdec_timestamptz_decode_should_fail('2016-0-02 23:23:59.123456+00:25:21')
|
190
|
-
textdec_timestamptz_decode_should_fail('2016-01-0 23:23:59.123456+00:25:21')
|
191
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 2:23:59.123456+00:25:21')
|
192
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:2:59.123456+00:25:21')
|
193
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:5.123456+00:25:21')
|
194
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.+00:25:21')
|
195
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+0:25:21')
|
196
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:2:21')
|
197
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:25:2')
|
198
|
-
end
|
199
|
-
it 'fails when the timestamp contains values with more digits than expected' do
|
200
|
-
textdec_timestamptz_decode_should_fail('2016-011-02 23:23:59.123456+00:25:21')
|
201
|
-
textdec_timestamptz_decode_should_fail('2016-01-022 23:23:59.123456+00:25:21')
|
202
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 233:23:59.123456+00:25:21')
|
203
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:233:59.123456+00:25:21')
|
204
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:599.123456+00:25:21')
|
205
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+000:25:21')
|
206
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:255:21')
|
207
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:25:211')
|
208
|
-
end
|
209
|
-
it 'fails when the timestamp contains values with invalid characters' do
|
210
|
-
str = '2013-01-02 23:23:59.123456+00:25:21'
|
211
|
-
str.length.times do |i|
|
212
|
-
textdec_timestamptz_decode_should_fail(str[0,i] + "x" + str[i+1..-1])
|
213
|
-
end
|
214
|
-
end
|
215
|
-
it 'fails when the timestamp contains leading characters' do
|
216
|
-
textdec_timestamptz_decode_should_fail(' 2016-01-02 23:23:59.123456')
|
217
|
-
end
|
218
|
-
it 'fails when the timestamp contains trailing characters' do
|
219
|
-
textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456 ')
|
220
|
-
end
|
221
|
-
it 'fails when the timestamp contains non ASCII character' do
|
222
|
-
textdec_timestamptz_decode_should_fail('2016-01ª02 23:23:59.123456')
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
context 'identifier quotation' do
|
227
|
-
it 'should build an array out of an quoted identifier string' do
|
228
|
-
quoted_type = PG::TextDecoder::Identifier.new
|
229
|
-
expect( quoted_type.decode(%["A.".".B"]) ).to eq( ["A.", ".B"] )
|
230
|
-
expect( quoted_type.decode(%["'A"".""B'"]) ).to eq( ['\'A"."B\''] )
|
231
|
-
end
|
232
|
-
|
233
|
-
it 'should split unquoted identifier string' do
|
234
|
-
quoted_type = PG::TextDecoder::Identifier.new
|
235
|
-
expect( quoted_type.decode(%[a.b]) ).to eq( ['a','b'] )
|
236
|
-
expect( quoted_type.decode(%[a]) ).to eq( ['a'] )
|
237
|
-
end
|
238
|
-
|
239
|
-
it 'should split identifier string with correct character encoding' do
|
240
|
-
quoted_type = PG::TextDecoder::Identifier.new
|
241
|
-
v = quoted_type.decode(%[Héllo].encode("iso-8859-1")).first
|
242
|
-
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
243
|
-
expect( v ).to eq( %[Héllo].encode(Encoding::ISO_8859_1) )
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
it "should raise when decode method is called with wrong args" do
|
248
|
-
expect{ textdec_int.decode() }.to raise_error(ArgumentError)
|
249
|
-
expect{ textdec_int.decode("123", 2, 3, 4) }.to raise_error(ArgumentError)
|
250
|
-
expect{ textdec_int.decode(2, 3, 4) }.to raise_error(TypeError)
|
251
|
-
expect( intdec_incrementer.decode(2, 3, 4) ).to eq( 3 )
|
252
|
-
end
|
253
|
-
|
254
|
-
it "should pass through nil values" do
|
255
|
-
expect( textdec_string.decode( nil )).to be_nil
|
256
|
-
expect( textdec_int.decode( nil )).to be_nil
|
257
|
-
end
|
258
|
-
|
259
|
-
it "should be defined on an encoder but not on a decoder instance" do
|
260
|
-
expect( textdec_int.respond_to?(:decode) ).to be_truthy
|
261
|
-
expect( textenc_int.respond_to?(:decode) ).to be_falsey
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
describe '#encode' do
|
266
|
-
it "should offer encode method for text type" do
|
267
|
-
res = textenc_int.encode(123)
|
268
|
-
expect( res ).to eq( "123" )
|
269
|
-
end
|
270
|
-
|
271
|
-
it "should offer encode method for binary type" do
|
272
|
-
res = binaryenc_int8.encode(123)
|
273
|
-
expect( res ).to eq( [123].pack("q>") )
|
274
|
-
end
|
275
|
-
|
276
|
-
it "should encode integers from string to binary format" do
|
277
|
-
expect( binaryenc_int2.encode(" -123 ") ).to eq( [-123].pack("s>") )
|
278
|
-
expect( binaryenc_int4.encode(" -123 ") ).to eq( [-123].pack("l>") )
|
279
|
-
expect( binaryenc_int8.encode(" -123 ") ).to eq( [-123].pack("q>") )
|
280
|
-
expect( binaryenc_int2.encode(" 123-xyz ") ).to eq( [123].pack("s>") )
|
281
|
-
expect( binaryenc_int4.encode(" 123-xyz ") ).to eq( [123].pack("l>") )
|
282
|
-
expect( binaryenc_int8.encode(" 123-xyz ") ).to eq( [123].pack("q>") )
|
283
|
-
end
|
284
|
-
|
285
|
-
it "should encode integers of different lengths to text format" do
|
286
|
-
30.times do |zeros|
|
287
|
-
expect( textenc_int.encode(10 ** zeros) ).to eq( "1" + "0"*zeros )
|
288
|
-
expect( textenc_int.encode(10 ** zeros - 1) ).to eq( zeros==0 ? "0" : "9"*zeros )
|
289
|
-
expect( textenc_int.encode(-10 ** zeros) ).to eq( "-1" + "0"*zeros )
|
290
|
-
expect( textenc_int.encode(-10 ** zeros + 1) ).to eq( zeros==0 ? "0" : "-" + "9"*zeros )
|
291
|
-
end
|
292
|
-
66.times do |bits|
|
293
|
-
expect( textenc_int.encode(2 ** bits) ).to eq( (2 ** bits).to_s )
|
294
|
-
expect( textenc_int.encode(2 ** bits - 1) ).to eq( (2 ** bits - 1).to_s )
|
295
|
-
expect( textenc_int.encode(-2 ** bits) ).to eq( (-2 ** bits).to_s )
|
296
|
-
expect( textenc_int.encode(-2 ** bits + 1) ).to eq( (-2 ** bits + 1).to_s )
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
it "should encode integers from string to text format" do
|
301
|
-
expect( textenc_int.encode(" -123 ") ).to eq( "-123" )
|
302
|
-
expect( textenc_int.encode(" 123-xyz ") ).to eq( "123" )
|
303
|
-
end
|
304
|
-
|
305
|
-
it "should encode boolean values" do
|
306
|
-
expect( textenc_boolean.encode(false) ).to eq( "f" )
|
307
|
-
expect( textenc_boolean.encode(true) ).to eq( "t" )
|
308
|
-
["any", :other, "value", 0, 1, 2].each do |value|
|
309
|
-
expect( textenc_boolean.encode(value) ).to eq( value.to_s )
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
it "should encode floats" do
|
314
|
-
expect( textenc_float.encode(0) ).to eq( "0.0" )
|
315
|
-
expect( textenc_float.encode(-1) ).to eq( "-1.0" )
|
316
|
-
expect( textenc_float.encode(-1.234567890123456789) ).to eq( "-1.234567890123457" )
|
317
|
-
expect( textenc_float.encode(9) ).to eq( "9.0" )
|
318
|
-
expect( textenc_float.encode(10) ).to eq( "10.0" )
|
319
|
-
expect( textenc_float.encode(-99) ).to eq( "-99.0" )
|
320
|
-
expect( textenc_float.encode(-100) ).to eq( "-100.0" )
|
321
|
-
expect( textenc_float.encode(999) ).to eq( "999.0" )
|
322
|
-
expect( textenc_float.encode(-1000) ).to eq( "-1000.0" )
|
323
|
-
expect( textenc_float.encode(1234.567890123456789) ).to eq( "1234.567890123457" )
|
324
|
-
expect( textenc_float.encode(-9999) ).to eq( "-9999.0" )
|
325
|
-
expect( textenc_float.encode(10000) ).to eq( "10000.0" )
|
326
|
-
expect( textenc_float.encode(99999) ).to eq( "99999.0" )
|
327
|
-
expect( textenc_float.encode(-100000) ).to eq( "-100000.0" )
|
328
|
-
expect( textenc_float.encode(-999999) ).to eq( "-999999.0" )
|
329
|
-
expect( textenc_float.encode(1000000) ).to eq( "1000000.0" )
|
330
|
-
expect( textenc_float.encode(9999999) ).to eq( "9999999.0" )
|
331
|
-
expect( textenc_float.encode(-100000000000000) ).to eq( "-100000000000000.0" )
|
332
|
-
expect( textenc_float.encode(123456789012345) ).to eq( "123456789012345.0" )
|
333
|
-
expect( textenc_float.encode(-999999999999999) ).to eq( "-999999999999999.0" )
|
334
|
-
expect( textenc_float.encode(1000000000000000) ).to eq( "1e15" )
|
335
|
-
expect( textenc_float.encode(-1234567890123456) ).to eq( "-1.234567890123456e15" )
|
336
|
-
expect( textenc_float.encode(9999999999999999) ).to eq( "1e16" )
|
337
|
-
|
338
|
-
expect( textenc_float.encode(-0.0) ).to eq( "0.0" )
|
339
|
-
expect( textenc_float.encode(0.1) ).to eq( "0.1" )
|
340
|
-
expect( textenc_float.encode(0.1234567890123456789) ).to eq( "0.1234567890123457" )
|
341
|
-
expect( textenc_float.encode(-0.9) ).to eq( "-0.9" )
|
342
|
-
expect( textenc_float.encode(-0.01234567890123456789) ).to eq( "-0.01234567890123457" )
|
343
|
-
expect( textenc_float.encode(0.09) ).to eq( "0.09" )
|
344
|
-
expect( textenc_float.encode(0.001234567890123456789) ).to eq( "0.001234567890123457" )
|
345
|
-
expect( textenc_float.encode(-0.009) ).to eq( "-0.009" )
|
346
|
-
expect( textenc_float.encode(-0.0001234567890123456789) ).to eq( "-0.0001234567890123457" )
|
347
|
-
expect( textenc_float.encode(0.0009) ).to eq( "0.0009" )
|
348
|
-
expect( textenc_float.encode(0.00001) ).to eq( "1e-5" )
|
349
|
-
expect( textenc_float.encode(0.00001234567890123456789) ).to eq( "1.234567890123457e-5" )
|
350
|
-
expect( textenc_float.encode(-0.00009) ).to eq( "-9e-5" )
|
351
|
-
expect( textenc_float.encode(-0.11) ).to eq( "-0.11" )
|
352
|
-
expect( textenc_float.encode(10.11) ).to eq( "10.11" )
|
353
|
-
expect( textenc_float.encode(-1.234567890123456789E-280) ).to eq( "-1.234567890123457e-280" )
|
354
|
-
expect( textenc_float.encode(-1.234567890123456789E280) ).to eq( "-1.234567890123457e280" )
|
355
|
-
expect( textenc_float.encode(9876543210987654321E280) ).to eq( "9.87654321098765e298" )
|
356
|
-
expect( textenc_float.encode(9876543210987654321E-400) ).to eq( "0.0" )
|
357
|
-
expect( textenc_float.encode(9876543210987654321E400) ).to eq( "Infinity" )
|
358
|
-
end
|
359
|
-
|
360
|
-
it "should encode special floats equally to Float#to_s" do
|
361
|
-
expect( textenc_float.encode(Float::INFINITY) ).to eq( Float::INFINITY.to_s )
|
362
|
-
expect( textenc_float.encode(-Float::INFINITY) ).to eq( (-Float::INFINITY).to_s )
|
363
|
-
expect( textenc_float.encode(-Float::NAN) ).to eq( Float::NAN.to_s )
|
364
|
-
end
|
365
|
-
|
366
|
-
it "should encode various inputs to numeric format" do
|
367
|
-
expect( textenc_numeric.encode(0) ).to eq( "0" )
|
368
|
-
expect( textenc_numeric.encode(1) ).to eq( "1" )
|
369
|
-
expect( textenc_numeric.encode(-12345678901234567890123) ).to eq( "-12345678901234567890123" )
|
370
|
-
expect( textenc_numeric.encode(0.0) ).to eq( "0.0" )
|
371
|
-
expect( textenc_numeric.encode(1.0) ).to eq( "1.0" )
|
372
|
-
expect( textenc_numeric.encode(-1.23456789012e45) ).to eq( "-1.23456789012e45" )
|
373
|
-
expect( textenc_numeric.encode(Float::NAN) ).to eq( Float::NAN.to_s )
|
374
|
-
expect( textenc_numeric.encode(BigDecimal(0)) ).to eq( "0.0" )
|
375
|
-
expect( textenc_numeric.encode(BigDecimal(1)) ).to eq( "1.0" )
|
376
|
-
expect( textenc_numeric.encode(BigDecimal("-12345678901234567890.1234567")) ).to eq( "-12345678901234567890.1234567" )
|
377
|
-
expect( textenc_numeric.encode(" 123 ") ).to eq( " 123 " )
|
378
|
-
end
|
379
|
-
|
380
|
-
it "encodes binary string to bytea" do
|
381
|
-
expect( textenc_bytea.encode("\x00\x01\x02\x03\xef".b) ).to eq( "\\x00010203ef" )
|
382
|
-
end
|
383
|
-
|
384
|
-
context 'timestamps' do
|
385
|
-
it 'encodes timestamps without timezone' do
|
386
|
-
expect( textenc_timestamp.encode(Time.new(2016,1,2, 23, 23, 59.123456, 3*60*60)) ).
|
387
|
-
to match( /^2016-01-02 23:23:59.12345\d+$/ )
|
388
|
-
expect( textenc_timestamp.encode(Time.new(2016,8,2, 23, 23, 59.123456, 3*60*60)) ).
|
389
|
-
to match( /^2016-08-02 23:23:59.12345\d+$/ )
|
390
|
-
end
|
391
|
-
it 'encodes timestamps with UTC timezone' do
|
392
|
-
expect( textenc_timestamputc.encode(Time.new(2016,1,2, 23, 23, 59.123456, 3*60*60)) ).
|
393
|
-
to match( /^2016-01-02 20:23:59.12345\d+$/ )
|
394
|
-
expect( textenc_timestamputc.encode(Time.new(2016,8,2, 23, 23, 59.123456, 3*60*60)) ).
|
395
|
-
to match( /^2016-08-02 20:23:59.12345\d+$/ )
|
396
|
-
end
|
397
|
-
it 'encodes timestamps with hour timezone' do
|
398
|
-
expect( textenc_timestamptz.encode(Time.new(2016,1,02, 23, 23, 59.123456, -4*60*60)) ).
|
399
|
-
to match( /^2016-01-02 23:23:59.12345\d+ \-04:00$/ )
|
400
|
-
expect( textenc_timestamptz.encode(Time.new(2016,8,02, 23, 23, 59.123456, 10*60*60)) ).
|
401
|
-
to match( /^2016-08-02 23:23:59.12345\d+ \+10:00$/ )
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
context 'identifier quotation' do
|
406
|
-
it 'should quote and escape identifier' do
|
407
|
-
quoted_type = PG::TextEncoder::Identifier.new
|
408
|
-
expect( quoted_type.encode(['schema','table','col']) ).to eq( %["schema"."table"."col"] )
|
409
|
-
expect( quoted_type.encode(['A.','.B']) ).to eq( %["A.".".B"] )
|
410
|
-
expect( quoted_type.encode(%['A"."B']) ).to eq( %["'A"".""B'"] )
|
411
|
-
expect( quoted_type.encode( nil ) ).to be_nil
|
412
|
-
end
|
413
|
-
|
414
|
-
it 'should quote identifiers with correct character encoding' do
|
415
|
-
quoted_type = PG::TextEncoder::Identifier.new
|
416
|
-
v = quoted_type.encode(['Héllo'], "iso-8859-1")
|
417
|
-
expect( v ).to eq( %["Héllo"].encode(Encoding::ISO_8859_1) )
|
418
|
-
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
419
|
-
end
|
420
|
-
|
421
|
-
it "will raise a TypeError for invalid arguments to quote_ident" do
|
422
|
-
quoted_type = PG::TextEncoder::Identifier.new
|
423
|
-
expect{ quoted_type.encode( [nil] ) }.to raise_error(TypeError)
|
424
|
-
expect{ quoted_type.encode( [['a']] ) }.to raise_error(TypeError)
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
it "should encode with ruby encoder" do
|
429
|
-
expect( intenc_incrementer.encode(3) ).to eq( "4 " )
|
430
|
-
end
|
431
|
-
|
432
|
-
it "should encode with ruby encoder and given character encoding" do
|
433
|
-
r = intenc_incrementer_with_encoding.encode(3, Encoding::CP850)
|
434
|
-
expect( r ).to eq( "4 CP850" )
|
435
|
-
expect( r.encoding ).to eq( Encoding::CP850 )
|
436
|
-
end
|
437
|
-
|
438
|
-
it "should return when ruby encoder returns non string values" do
|
439
|
-
expect( intenc_incrementer_with_int_result.encode(3) ).to eq( 4 )
|
440
|
-
end
|
441
|
-
|
442
|
-
it "should pass through nil values" do
|
443
|
-
expect( textenc_string.encode( nil )).to be_nil
|
444
|
-
expect( textenc_int.encode( nil )).to be_nil
|
445
|
-
end
|
446
|
-
|
447
|
-
it "should be defined on a decoder but not on an encoder instance" do
|
448
|
-
expect( textenc_int.respond_to?(:encode) ).to be_truthy
|
449
|
-
expect( textdec_int.respond_to?(:encode) ).to be_falsey
|
450
|
-
end
|
451
|
-
end
|
452
|
-
|
453
|
-
it "should be possible to marshal encoders" do
|
454
|
-
mt = Marshal.dump(textenc_int)
|
455
|
-
lt = Marshal.load(mt)
|
456
|
-
expect( lt.to_h ).to eq( textenc_int.to_h )
|
457
|
-
end
|
458
|
-
|
459
|
-
it "should be possible to marshal decoders" do
|
460
|
-
mt = Marshal.dump(textdec_int)
|
461
|
-
lt = Marshal.load(mt)
|
462
|
-
expect( lt.to_h ).to eq( textdec_int.to_h )
|
463
|
-
end
|
464
|
-
|
465
|
-
it "should respond to to_h" do
|
466
|
-
expect( textenc_int.to_h ).to eq( {
|
467
|
-
name: 'Integer', oid: 23, format: 0, flags: 0
|
468
|
-
} )
|
469
|
-
end
|
470
|
-
|
471
|
-
it "should have reasonable default values" do
|
472
|
-
t = PG::TextEncoder::String.new
|
473
|
-
expect( t.format ).to eq( 0 )
|
474
|
-
expect( t.oid ).to eq( 0 )
|
475
|
-
expect( t.name ).to be_nil
|
476
|
-
|
477
|
-
t = PG::BinaryEncoder::Int4.new
|
478
|
-
expect( t.format ).to eq( 1 )
|
479
|
-
expect( t.oid ).to eq( 0 )
|
480
|
-
expect( t.name ).to be_nil
|
481
|
-
|
482
|
-
t = PG::TextDecoder::String.new
|
483
|
-
expect( t.format ).to eq( 0 )
|
484
|
-
expect( t.oid ).to eq( 0 )
|
485
|
-
expect( t.name ).to be_nil
|
486
|
-
|
487
|
-
t = PG::BinaryDecoder::String.new
|
488
|
-
expect( t.format ).to eq( 1 )
|
489
|
-
expect( t.oid ).to eq( 0 )
|
490
|
-
expect( t.name ).to be_nil
|
491
|
-
end
|
492
|
-
end
|
493
|
-
|
494
|
-
describe PG::CompositeCoder do
|
495
|
-
describe "Array types" do
|
496
|
-
let!(:textenc_string_array) { PG::TextEncoder::Array.new elements_type: textenc_string }
|
497
|
-
let!(:textdec_string_array) { PG::TextDecoder::Array.new elements_type: textdec_string }
|
498
|
-
let!(:textdec_string_array_raise) { PG::TextDecoder::Array.new elements_type: textdec_string, flags: PG::Coder:: FORMAT_ERROR_TO_RAISE }
|
499
|
-
let!(:textenc_int_array) { PG::TextEncoder::Array.new elements_type: textenc_int, needs_quotation: false }
|
500
|
-
let!(:textdec_int_array) { PG::TextDecoder::Array.new elements_type: textdec_int, needs_quotation: false }
|
501
|
-
let!(:textenc_float_array) { PG::TextEncoder::Array.new elements_type: textenc_float, needs_quotation: false }
|
502
|
-
let!(:textdec_float_array) { PG::TextDecoder::Array.new elements_type: textdec_float, needs_quotation: false }
|
503
|
-
let!(:textenc_timestamp_array) { PG::TextEncoder::Array.new elements_type: textenc_timestamp, needs_quotation: false }
|
504
|
-
let!(:textdec_timestamp_array) { PG::TextDecoder::Array.new elements_type: textdec_timestamp, needs_quotation: false }
|
505
|
-
let!(:textenc_string_array_with_delimiter) { PG::TextEncoder::Array.new elements_type: textenc_string, delimiter: ';' }
|
506
|
-
let!(:textdec_string_array_with_delimiter) { PG::TextDecoder::Array.new elements_type: textdec_string, delimiter: ';' }
|
507
|
-
let!(:textdec_bytea_array) { PG::TextDecoder::Array.new elements_type: textdec_bytea }
|
508
|
-
|
509
|
-
#
|
510
|
-
# Array parser specs are thankfully borrowed from here:
|
511
|
-
# https://github.com/dockyard/pg_array_parser
|
512
|
-
#
|
513
|
-
describe '#decode' do
|
514
|
-
context 'one dimensional arrays' do
|
515
|
-
context 'empty' do
|
516
|
-
it 'returns an empty array' do
|
517
|
-
expect( textdec_string_array.decode(%[{}]) ).to eq( [] )
|
518
|
-
end
|
519
|
-
end
|
520
|
-
|
521
|
-
context 'no strings' do
|
522
|
-
it 'returns an array of strings' do
|
523
|
-
expect( textdec_string_array.decode(%[{1,2,3}]) ).to eq( ['1','2','3'] )
|
524
|
-
end
|
525
|
-
end
|
526
|
-
|
527
|
-
context 'NULL values' do
|
528
|
-
it 'returns an array of strings, with nils replacing NULL characters' do
|
529
|
-
expect( textdec_string_array.decode(%[{1,NULL,NULL}]) ).to eq( ['1',nil,nil] )
|
530
|
-
end
|
531
|
-
end
|
532
|
-
|
533
|
-
context 'quoted NULL' do
|
534
|
-
it 'returns an array with the word NULL' do
|
535
|
-
expect( textdec_string_array.decode(%[{1,"NULL",3}]) ).to eq( ['1','NULL','3'] )
|
536
|
-
end
|
537
|
-
end
|
538
|
-
|
539
|
-
context 'strings' do
|
540
|
-
it 'returns an array of strings when containing commas in a quoted string' do
|
541
|
-
expect( textdec_string_array.decode(%[{1,"2,3",4}]) ).to eq( ['1','2,3','4'] )
|
542
|
-
end
|
543
|
-
|
544
|
-
it 'returns an array of strings when containing an escaped quote' do
|
545
|
-
expect( textdec_string_array.decode(%[{1,"2\\",3",4}]) ).to eq( ['1','2",3','4'] )
|
546
|
-
end
|
547
|
-
|
548
|
-
it 'returns an array of strings when containing an escaped backslash' do
|
549
|
-
expect( textdec_string_array.decode(%[{1,"2\\\\",3,4}]) ).to eq( ['1','2\\','3','4'] )
|
550
|
-
expect( textdec_string_array.decode(%[{1,"2\\\\\\",3",4}]) ).to eq( ['1','2\\",3','4'] )
|
551
|
-
end
|
552
|
-
|
553
|
-
it 'returns an array containing empty strings' do
|
554
|
-
expect( textdec_string_array.decode(%[{1,"",3,""}]) ).to eq( ['1', '', '3', ''] )
|
555
|
-
end
|
556
|
-
|
557
|
-
it 'returns an array containing unicode strings' do
|
558
|
-
expect( textdec_string_array.decode(%[{"Paragraph 399(b)(i) – “valid leave” – meaning"}]) ).to eq(['Paragraph 399(b)(i) – “valid leave” – meaning'])
|
559
|
-
end
|
560
|
-
|
561
|
-
it 'respects a different delimiter' do
|
562
|
-
expect( textdec_string_array_with_delimiter.decode(%[{1;2;3}]) ).to eq( ['1','2','3'] )
|
563
|
-
end
|
564
|
-
|
565
|
-
it 'ignores array dimensions' do
|
566
|
-
expect( textdec_string_array.decode(%[[2:4]={1,2,3}]) ).to eq( ['1','2','3'] )
|
567
|
-
expect( textdec_string_array.decode(%[[]={1,2,3}]) ).to eq( ['1','2','3'] )
|
568
|
-
expect( textdec_string_array.decode(%[ [-1:+2]= {4,3,2,1}]) ).to eq( ['4','3','2','1'] )
|
569
|
-
end
|
570
|
-
|
571
|
-
it 'ignores spaces after array' do
|
572
|
-
expect( textdec_string_array.decode(%[[2:4]={1,2,3} ]) ).to eq( ['1','2','3'] )
|
573
|
-
expect( textdec_string_array.decode(%[{1,2,3} ]) ).to eq( ['1','2','3'] )
|
574
|
-
end
|
575
|
-
|
576
|
-
describe "with malformed syntax are deprecated" do
|
577
|
-
it 'accepts broken array dimensions' do
|
578
|
-
expect( textdec_string_array.decode(%([2:4={1,2,3})) ).to eq([['1','2','3']])
|
579
|
-
expect( textdec_string_array.decode(%(2:4]={1,2,3})) ).to eq([['1','2','3']])
|
580
|
-
expect( textdec_string_array.decode(%(={1,2,3})) ).to eq([['1','2','3']])
|
581
|
-
expect( textdec_string_array.decode(%([x]={1,2,3})) ).to eq([['1','2','3']])
|
582
|
-
expect( textdec_string_array.decode(%([]{1,2,3})) ).to eq([['1','2','3']])
|
583
|
-
expect( textdec_string_array.decode(%(1,2,3)) ).to eq(['','2'])
|
584
|
-
end
|
585
|
-
|
586
|
-
it 'accepts malformed arrays' do
|
587
|
-
expect( textdec_string_array.decode(%({1,2,3)) ).to eq(['1','2'])
|
588
|
-
expect( textdec_string_array.decode(%({1,2,3}})) ).to eq(['1','2','3'])
|
589
|
-
expect( textdec_string_array.decode(%({1,2,3}x)) ).to eq(['1','2','3'])
|
590
|
-
expect( textdec_string_array.decode(%({{1,2},{2,3})) ).to eq([['1','2'],['2','3']])
|
591
|
-
expect( textdec_string_array.decode(%({{1,2},{2,3}}x)) ).to eq([['1','2'],['2','3']])
|
592
|
-
expect( textdec_string_array.decode(%({[1,2},{2,3}}})) ).to eq(['[1','2'])
|
593
|
-
end
|
594
|
-
end
|
595
|
-
|
596
|
-
describe "with malformed syntax are raised with pg-2.0+" do
|
597
|
-
it 'complains about broken array dimensions' do
|
598
|
-
expect{ textdec_string_array_raise.decode(%([2:4={1,2,3})) }.to raise_error(TypeError)
|
599
|
-
expect{ textdec_string_array_raise.decode(%(2:4]={1,2,3})) }.to raise_error(TypeError)
|
600
|
-
expect{ textdec_string_array_raise.decode(%(={1,2,3})) }.to raise_error(TypeError)
|
601
|
-
expect{ textdec_string_array_raise.decode(%([x]={1,2,3})) }.to raise_error(TypeError)
|
602
|
-
expect{ textdec_string_array_raise.decode(%([]{1,2,3})) }.to raise_error(TypeError)
|
603
|
-
expect{ textdec_string_array_raise.decode(%(1,2,3)) }.to raise_error(TypeError)
|
604
|
-
end
|
605
|
-
|
606
|
-
it 'complains about malformed array' do
|
607
|
-
expect{ textdec_string_array_raise.decode(%({1,2,3)) }.to raise_error(TypeError)
|
608
|
-
expect{ textdec_string_array_raise.decode(%({1,2,3}})) }.to raise_error(TypeError)
|
609
|
-
expect{ textdec_string_array_raise.decode(%({1,2,3}x)) }.to raise_error(TypeError)
|
610
|
-
expect{ textdec_string_array_raise.decode(%({{1,2},{2,3})) }.to raise_error(TypeError)
|
611
|
-
expect{ textdec_string_array_raise.decode(%({{1,2},{2,3}}x)) }.to raise_error(TypeError)
|
612
|
-
expect{ textdec_string_array_raise.decode(%({[1,2},{2,3}}})) }.to raise_error(TypeError)
|
613
|
-
end
|
614
|
-
end
|
615
|
-
end
|
616
|
-
|
617
|
-
context 'bytea' do
|
618
|
-
it 'returns an array of binary strings' do
|
619
|
-
expect( textdec_bytea_array.decode(%[{"\\\\x00010203EF","2,3",\\377}]) ).to eq( ["\x00\x01\x02\x03\xef".b,"2,3".b,"\xff".b] )
|
620
|
-
end
|
621
|
-
end
|
622
|
-
|
623
|
-
end
|
624
|
-
|
625
|
-
context 'two dimensional arrays' do
|
626
|
-
context 'empty' do
|
627
|
-
it 'returns an empty array' do
|
628
|
-
expect( textdec_string_array.decode(%[{{}}]) ).to eq( [[]] )
|
629
|
-
expect( textdec_string_array.decode(%[{{},{}}]) ).to eq( [[],[]] )
|
630
|
-
end
|
631
|
-
end
|
632
|
-
context 'no strings' do
|
633
|
-
it 'returns an array of strings with a sub array' do
|
634
|
-
expect( textdec_string_array.decode(%[{1,{2,3},4}]) ).to eq( ['1',['2','3'],'4'] )
|
635
|
-
end
|
636
|
-
end
|
637
|
-
context 'strings' do
|
638
|
-
it 'returns an array of strings with a sub array' do
|
639
|
-
expect( textdec_string_array.decode(%[{1,{"2,3"},4}]) ).to eq( ['1',['2,3'],'4'] )
|
640
|
-
end
|
641
|
-
it 'returns an array of strings with a sub array and a quoted }' do
|
642
|
-
expect( textdec_string_array.decode(%[{1,{"2,}3",NULL},4}]) ).to eq( ['1',['2,}3',nil],'4'] )
|
643
|
-
end
|
644
|
-
it 'returns an array of strings with a sub array and a quoted {' do
|
645
|
-
expect( textdec_string_array.decode(%[{1,{"2,{3"},4}]) ).to eq( ['1',['2,{3'],'4'] )
|
646
|
-
end
|
647
|
-
it 'returns an array of strings with a sub array and a quoted { and escaped quote' do
|
648
|
-
expect( textdec_string_array.decode(%[{1,{"2\\",{3"},4}]) ).to eq( ['1',['2",{3'],'4'] )
|
649
|
-
end
|
650
|
-
it 'returns an array of strings with a sub array with empty strings' do
|
651
|
-
expect( textdec_string_array.decode(%[{1,{""},4,{""}}]) ).to eq( ['1',[''],'4',['']] )
|
652
|
-
end
|
653
|
-
end
|
654
|
-
context 'timestamps' do
|
655
|
-
it 'decodes an array of timestamps with sub arrays' do
|
656
|
-
expect( textdec_timestamp_array.decode('{2014-12-31 00:00:00,{NULL,2016-01-02 23:23:59.0000000}}') ).
|
657
|
-
to eq( [Time.new(2014,12,31),[nil, Time.new(2016,01,02, 23, 23, 59)]] )
|
658
|
-
end
|
659
|
-
end
|
660
|
-
end
|
661
|
-
context 'three dimensional arrays' do
|
662
|
-
context 'empty' do
|
663
|
-
it 'returns an empty array' do
|
664
|
-
expect( textdec_string_array.decode(%[{{{}}}]) ).to eq( [[[]]] )
|
665
|
-
expect( textdec_string_array.decode(%[{{{},{}},{{},{}}}]) ).to eq( [[[],[]],[[],[]]] )
|
666
|
-
end
|
667
|
-
end
|
668
|
-
it 'returns an array of strings with sub arrays' do
|
669
|
-
expect( textdec_string_array.decode(%[{1,{2,{3,4}},{NULL,6},7}]) ).to eq( ['1',['2',['3','4']],[nil,'6'],'7'] )
|
670
|
-
end
|
671
|
-
end
|
672
|
-
|
673
|
-
it 'should decode array of types with decoder in ruby space' do
|
674
|
-
array_type = PG::TextDecoder::Array.new elements_type: intdec_incrementer
|
675
|
-
expect( array_type.decode(%[{3,4}]) ).to eq( [4,5] )
|
676
|
-
end
|
677
|
-
|
678
|
-
it 'should decode array of nil types' do
|
679
|
-
array_type = PG::TextDecoder::Array.new elements_type: nil
|
680
|
-
expect( array_type.decode(%[{3,4}]) ).to eq( ['3','4'] )
|
681
|
-
end
|
682
|
-
end
|
683
|
-
|
684
|
-
describe '#encode' do
|
685
|
-
context 'three dimensional arrays' do
|
686
|
-
it 'encodes an array of strings and numbers with sub arrays' do
|
687
|
-
expect( textenc_string_array.encode(['1',['2',['3','4']],[nil,6],7.8]) ).to eq( %[{1,{2,{3,4}},{NULL,6},7.8}] )
|
688
|
-
end
|
689
|
-
it 'encodes an array of strings with quotes' do
|
690
|
-
expect( textenc_string_array.encode(['',[' ',['{','}','\\',',','"','\t']]]) ).to eq( %[{"",{" ",{"{","}","\\\\",",","\\"","\\\\t"}}}] )
|
691
|
-
end
|
692
|
-
it 'encodes an array of int8 with sub arrays' do
|
693
|
-
expect( textenc_int_array.encode([1,[2,[3,4]],[nil,6],7]) ).to eq( %[{1,{2,{3,4}},{NULL,6},7}] )
|
694
|
-
end
|
695
|
-
it 'encodes an array of int8 with strings' do
|
696
|
-
expect( textenc_int_array.encode(['1',['2'],'3']) ).to eq( %[{1,{2},3}] )
|
697
|
-
end
|
698
|
-
it 'encodes an array of float8 with sub arrays' do
|
699
|
-
expect( textenc_float_array.encode([1000.11,[-0.00000221,[3.31,-441]],[nil,6.61],-7.71]) ).to match(Regexp.new(%[^{1000.1*,{-2.2*e-*6,{3.3*,-441.0}},{NULL,6.6*},-7.7*}$].gsub(/([\.\+\{\}\,])/, "\\\\\\1").gsub(/\*/, "\\d*")))
|
700
|
-
end
|
701
|
-
end
|
702
|
-
context 'two dimensional arrays' do
|
703
|
-
it 'encodes an array of timestamps with sub arrays' do
|
704
|
-
expect( textenc_timestamp_array.encode([Time.new(2014,12,31),[nil, Time.new(2016,01,02, 23, 23, 59.99)]]) ).
|
705
|
-
to eq( %[{2014-12-31 00:00:00.000000000,{NULL,2016-01-02 23:23:59.990000000}}] )
|
706
|
-
end
|
707
|
-
end
|
708
|
-
context 'one dimensional array' do
|
709
|
-
it 'can encode empty arrays' do
|
710
|
-
expect( textenc_int_array.encode([]) ).to eq( '{}' )
|
711
|
-
expect( textenc_string_array.encode([]) ).to eq( '{}' )
|
712
|
-
end
|
713
|
-
it 'encodes an array of NULL strings w/wo quotes' do
|
714
|
-
expect( textenc_string_array.encode(['NUL', 'NULL', 'NULLL', 'nul', 'null', 'nulll']) ).to eq( %[{NUL,"NULL",NULLL,nul,"null",nulll}] )
|
715
|
-
end
|
716
|
-
it 'respects a different delimiter' do
|
717
|
-
expect( textenc_string_array_with_delimiter.encode(['a','b,','c']) ).to eq( '{a;b,;c}' )
|
718
|
-
end
|
719
|
-
end
|
720
|
-
|
721
|
-
context 'array of types with encoder in ruby space' do
|
722
|
-
it 'encodes with quotation and default character encoding' do
|
723
|
-
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
|
724
|
-
r = array_type.encode([3,4])
|
725
|
-
expect( r ).to eq( %[{"4 ","5 "}] )
|
726
|
-
expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
|
727
|
-
end
|
728
|
-
|
729
|
-
it 'encodes with quotation and given character encoding' do
|
730
|
-
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
|
731
|
-
r = array_type.encode([3,4], Encoding::CP850)
|
732
|
-
expect( r ).to eq( %[{"4 ","5 "}] )
|
733
|
-
expect( r.encoding ).to eq( Encoding::CP850 )
|
734
|
-
end
|
735
|
-
|
736
|
-
it 'encodes without quotation' do
|
737
|
-
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: false
|
738
|
-
expect( array_type.encode([3,4]) ).to eq( %[{4 ,5 }] )
|
739
|
-
end
|
740
|
-
|
741
|
-
it 'encodes with default character encoding' do
|
742
|
-
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
|
743
|
-
r = array_type.encode([3,4])
|
744
|
-
expect( r ).to eq( %[{"4 ASCII-8BIT","5 ASCII-8BIT"}] )
|
745
|
-
expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
|
746
|
-
end
|
747
|
-
|
748
|
-
it 'encodes with given character encoding' do
|
749
|
-
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
|
750
|
-
r = array_type.encode([3,4], Encoding::CP850)
|
751
|
-
expect( r ).to eq( %[{"4 CP850","5 CP850"}] )
|
752
|
-
expect( r.encoding ).to eq( Encoding::CP850 )
|
753
|
-
end
|
754
|
-
|
755
|
-
it "should raise when ruby encoder returns non string values" do
|
756
|
-
array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_int_result, needs_quotation: false
|
757
|
-
expect{ array_type.encode([3,4]) }.to raise_error(TypeError)
|
758
|
-
end
|
759
|
-
end
|
760
|
-
|
761
|
-
it "should pass through non Array inputs" do
|
762
|
-
expect( textenc_float_array.encode("text") ).to eq( "text" )
|
763
|
-
expect( textenc_float_array.encode(1234) ).to eq( "1234" )
|
764
|
-
end
|
765
|
-
|
766
|
-
context 'literal quotation' do
|
767
|
-
it 'should quote and escape literals' do
|
768
|
-
quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
|
769
|
-
expect( quoted_type.encode(["'A\",","\\B'"]) ).to eq( %['{"''A\\",","\\\\B''"}'] )
|
770
|
-
end
|
771
|
-
|
772
|
-
it 'should quote literals with correct character encoding' do
|
773
|
-
quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
|
774
|
-
v = quoted_type.encode(["Héllo"], "iso-8859-1")
|
775
|
-
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
776
|
-
expect( v ).to eq( %['{Héllo}'].encode(Encoding::ISO_8859_1) )
|
777
|
-
end
|
778
|
-
end
|
779
|
-
end
|
780
|
-
|
781
|
-
it "should be possible to marshal encoders" do
|
782
|
-
mt = Marshal.dump(textenc_int_array)
|
783
|
-
lt = Marshal.load(mt)
|
784
|
-
expect( lt.to_h ).to eq( textenc_int_array.to_h )
|
785
|
-
end
|
786
|
-
|
787
|
-
it "should be possible to marshal decoders" do
|
788
|
-
mt = Marshal.dump(textdec_string_array_raise)
|
789
|
-
lt = Marshal.load(mt)
|
790
|
-
expect( lt.to_h ).to eq( textdec_string_array_raise.to_h )
|
791
|
-
end
|
792
|
-
|
793
|
-
it "should respond to to_h" do
|
794
|
-
expect( textenc_int_array.to_h ).to eq( {
|
795
|
-
name: nil, oid: 0, format: 0, flags: 0,
|
796
|
-
elements_type: textenc_int, needs_quotation: false, delimiter: ','
|
797
|
-
} )
|
798
|
-
end
|
799
|
-
|
800
|
-
it "shouldn't accept invalid elements_types" do
|
801
|
-
expect{ PG::TextEncoder::Array.new elements_type: false }.to raise_error(TypeError)
|
802
|
-
end
|
803
|
-
|
804
|
-
it "should have reasonable default values" do
|
805
|
-
t = PG::TextEncoder::Array.new
|
806
|
-
expect( t.format ).to eq( 0 )
|
807
|
-
expect( t.oid ).to eq( 0 )
|
808
|
-
expect( t.name ).to be_nil
|
809
|
-
expect( t.needs_quotation? ).to eq( true )
|
810
|
-
expect( t.delimiter ).to eq( ',' )
|
811
|
-
expect( t.elements_type ).to be_nil
|
812
|
-
end
|
813
|
-
end
|
814
|
-
|
815
|
-
it "should encode Strings as base64 in TextEncoder" do
|
816
|
-
e = PG::TextEncoder::ToBase64.new
|
817
|
-
expect( e.encode("") ).to eq("")
|
818
|
-
expect( e.encode("x") ).to eq("eA==")
|
819
|
-
expect( e.encode("xx") ).to eq("eHg=")
|
820
|
-
expect( e.encode("xxx") ).to eq("eHh4")
|
821
|
-
expect( e.encode("xxxx") ).to eq("eHh4eA==")
|
822
|
-
expect( e.encode("xxxxx") ).to eq("eHh4eHg=")
|
823
|
-
expect( e.encode("\0\n\t") ).to eq("AAoJ")
|
824
|
-
expect( e.encode("(\xFBm") ).to eq("KPtt")
|
825
|
-
end
|
826
|
-
|
827
|
-
it 'should encode Strings as base64 with correct character encoding' do
|
828
|
-
e = PG::TextEncoder::ToBase64.new
|
829
|
-
v = e.encode("Héllo".encode("utf-16le"), "iso-8859-1")
|
830
|
-
expect( v ).to eq("SOlsbG8=")
|
831
|
-
expect( v.encoding ).to eq(Encoding::ISO_8859_1)
|
832
|
-
end
|
833
|
-
|
834
|
-
it "should encode Strings as base64 in BinaryDecoder" do
|
835
|
-
e = PG::BinaryDecoder::ToBase64.new
|
836
|
-
expect( e.decode("x") ).to eq("eA==")
|
837
|
-
v = e.decode("Héllo".encode("utf-16le"))
|
838
|
-
expect( v ).to eq("SADpAGwAbABvAA==")
|
839
|
-
expect( v.encoding ).to eq(Encoding::ASCII_8BIT)
|
840
|
-
end
|
841
|
-
|
842
|
-
it "should encode Integers as base64" do
|
843
|
-
# Not really useful, but ensures that two-pass element and composite element encoders work.
|
844
|
-
e = PG::TextEncoder::ToBase64.new( elements_type: PG::TextEncoder::Array.new( elements_type: PG::TextEncoder::Integer.new, needs_quotation: false ))
|
845
|
-
expect( e.encode([1]) ).to eq(["{1}"].pack("m").chomp)
|
846
|
-
expect( e.encode([12]) ).to eq(["{12}"].pack("m").chomp)
|
847
|
-
expect( e.encode([123]) ).to eq(["{123}"].pack("m").chomp)
|
848
|
-
expect( e.encode([1234]) ).to eq(["{1234}"].pack("m").chomp)
|
849
|
-
expect( e.encode([12345]) ).to eq(["{12345}"].pack("m").chomp)
|
850
|
-
expect( e.encode([123456]) ).to eq(["{123456}"].pack("m").chomp)
|
851
|
-
expect( e.encode([1234567]) ).to eq(["{1234567}"].pack("m").chomp)
|
852
|
-
end
|
853
|
-
|
854
|
-
it "should decode base64 to Strings in TextDecoder" do
|
855
|
-
e = PG::TextDecoder::FromBase64.new
|
856
|
-
expect( e.decode("") ).to eq("")
|
857
|
-
expect( e.decode("eA==") ).to eq("x")
|
858
|
-
expect( e.decode("eHg=") ).to eq("xx")
|
859
|
-
expect( e.decode("eHh4") ).to eq("xxx")
|
860
|
-
expect( e.decode("eHh4eA==") ).to eq("xxxx")
|
861
|
-
expect( e.decode("eHh4eHg=") ).to eq("xxxxx")
|
862
|
-
expect( e.decode("AAoJ") ).to eq("\0\n\t")
|
863
|
-
expect( e.decode("KPtt") ).to eq("(\xFBm")
|
864
|
-
end
|
865
|
-
|
866
|
-
it "should decode base64 in BinaryEncoder" do
|
867
|
-
e = PG::BinaryEncoder::FromBase64.new
|
868
|
-
expect( e.encode("eA==") ).to eq("x")
|
869
|
-
|
870
|
-
e = PG::BinaryEncoder::FromBase64.new( elements_type: PG::TextEncoder::Integer.new )
|
871
|
-
expect( e.encode(124) ).to eq("124=".unpack("m")[0])
|
872
|
-
end
|
873
|
-
|
874
|
-
it "should decode base64 to Integers" do
|
875
|
-
# Not really useful, but ensures that composite element encoders work.
|
876
|
-
e = PG::TextDecoder::FromBase64.new( elements_type: PG::TextDecoder::Array.new( elements_type: PG::TextDecoder::Integer.new ))
|
877
|
-
expect( e.decode(["{1}"].pack("m")) ).to eq([1])
|
878
|
-
expect( e.decode(["{12}"].pack("m")) ).to eq([12])
|
879
|
-
expect( e.decode(["{123}"].pack("m")) ).to eq([123])
|
880
|
-
expect( e.decode(["{1234}"].pack("m")) ).to eq([1234])
|
881
|
-
expect( e.decode(["{12345}"].pack("m")) ).to eq([12345])
|
882
|
-
expect( e.decode(["{123456}"].pack("m")) ).to eq([123456])
|
883
|
-
expect( e.decode(["{1234567}"].pack("m")) ).to eq([1234567])
|
884
|
-
expect( e.decode(["{12345678}"].pack("m")) ).to eq([12345678])
|
885
|
-
|
886
|
-
e = PG::TextDecoder::FromBase64.new( elements_type: PG::BinaryDecoder::Integer.new )
|
887
|
-
expect( e.decode("ALxhTg==") ).to eq(12345678)
|
888
|
-
end
|
889
|
-
|
890
|
-
it "should decode base64 with garbage" do
|
891
|
-
e = PG::TextDecoder::FromBase64.new format: 1
|
892
|
-
expect( e.decode("=") ).to eq("=".unpack("m")[0])
|
893
|
-
expect( e.decode("==") ).to eq("==".unpack("m")[0])
|
894
|
-
expect( e.decode("===") ).to eq("===".unpack("m")[0])
|
895
|
-
expect( e.decode("====") ).to eq("====".unpack("m")[0])
|
896
|
-
expect( e.decode("a=") ).to eq("a=".unpack("m")[0])
|
897
|
-
expect( e.decode("a==") ).to eq("a==".unpack("m")[0])
|
898
|
-
expect( e.decode("a===") ).to eq("a===".unpack("m")[0])
|
899
|
-
expect( e.decode("a====") ).to eq("a====".unpack("m")[0])
|
900
|
-
expect( e.decode("aa=") ).to eq("aa=".unpack("m")[0])
|
901
|
-
expect( e.decode("aa==") ).to eq("aa==".unpack("m")[0])
|
902
|
-
expect( e.decode("aa===") ).to eq("aa===".unpack("m")[0])
|
903
|
-
expect( e.decode("aa====") ).to eq("aa====".unpack("m")[0])
|
904
|
-
expect( e.decode("aaa=") ).to eq("aaa=".unpack("m")[0])
|
905
|
-
expect( e.decode("aaa==") ).to eq("aaa==".unpack("m")[0])
|
906
|
-
expect( e.decode("aaa===") ).to eq("aaa===".unpack("m")[0])
|
907
|
-
expect( e.decode("aaa====") ).to eq("aaa====".unpack("m")[0])
|
908
|
-
expect( e.decode("=aa") ).to eq("=aa=".unpack("m")[0])
|
909
|
-
expect( e.decode("=aa=") ).to eq("=aa=".unpack("m")[0])
|
910
|
-
expect( e.decode("=aa==") ).to eq("=aa==".unpack("m")[0])
|
911
|
-
expect( e.decode("=aa===") ).to eq("=aa===".unpack("m")[0])
|
912
|
-
end
|
913
|
-
end
|
914
|
-
|
915
|
-
describe PG::CopyCoder do
|
916
|
-
describe PG::TextEncoder::CopyRow do
|
917
|
-
context "with default typemap" do
|
918
|
-
let!(:encoder) do
|
919
|
-
PG::TextEncoder::CopyRow.new
|
920
|
-
end
|
921
|
-
|
922
|
-
it "should encode different types of Ruby objects" do
|
923
|
-
expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
|
924
|
-
to eq("xyz\t123\t2456\t34567\t456789\t5678901\t[1, 2, 3]\t12.1\tabcdefg\t\\N\n")
|
925
|
-
end
|
926
|
-
|
927
|
-
it 'should output a string with correct character encoding' do
|
928
|
-
v = encoder.encode(["Héllo"], "iso-8859-1")
|
929
|
-
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
930
|
-
expect( v ).to eq( "Héllo\n".encode(Encoding::ISO_8859_1) )
|
931
|
-
end
|
932
|
-
end
|
933
|
-
|
934
|
-
context "with TypeMapByClass" do
|
935
|
-
let!(:tm) do
|
936
|
-
tm = PG::TypeMapByClass.new
|
937
|
-
tm[Integer] = textenc_int
|
938
|
-
tm[Float] = intenc_incrementer
|
939
|
-
tm[Array] = PG::TextEncoder::Array.new elements_type: textenc_string
|
940
|
-
tm
|
941
|
-
end
|
942
|
-
let!(:encoder) do
|
943
|
-
PG::TextEncoder::CopyRow.new type_map: tm
|
944
|
-
end
|
945
|
-
|
946
|
-
it "should have reasonable default values" do
|
947
|
-
expect( encoder.name ).to be_nil
|
948
|
-
expect( encoder.delimiter ).to eq( "\t" )
|
949
|
-
expect( encoder.null_string ).to eq( "\\N" )
|
950
|
-
end
|
951
|
-
|
952
|
-
it "copies all attributes with #dup" do
|
953
|
-
encoder.name = "test"
|
954
|
-
encoder.delimiter = "#"
|
955
|
-
encoder.null_string = "NULL"
|
956
|
-
encoder.type_map = PG::TypeMapByColumn.new []
|
957
|
-
encoder2 = encoder.dup
|
958
|
-
expect( encoder.object_id ).to_not eq( encoder2.object_id )
|
959
|
-
expect( encoder2.name ).to eq( "test" )
|
960
|
-
expect( encoder2.delimiter ).to eq( "#" )
|
961
|
-
expect( encoder2.null_string ).to eq( "NULL" )
|
962
|
-
expect( encoder2.type_map ).to be_a_kind_of( PG::TypeMapByColumn )
|
963
|
-
end
|
964
|
-
|
965
|
-
describe '#encode' do
|
966
|
-
it "should encode different types of Ruby objects" do
|
967
|
-
expect( encoder.encode([]) ).to eq("\n")
|
968
|
-
expect( encoder.encode(["a"]) ).to eq("a\n")
|
969
|
-
expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
|
970
|
-
to eq("xyz\t123\t2456\t34567\t456789\t5678901\t{1,2,3}\t13 \tabcdefg\t\\N\n")
|
971
|
-
end
|
972
|
-
|
973
|
-
it "should escape special characters" do
|
974
|
-
expect( encoder.encode([" \0\t\n\r\\"]) ).to eq(" \0#\t#\n#\r#\\\n".gsub("#", "\\"))
|
975
|
-
end
|
976
|
-
|
977
|
-
it "should escape with different delimiter" do
|
978
|
-
encoder.delimiter = " "
|
979
|
-
encoder.null_string = "NULL"
|
980
|
-
expect( encoder.encode([nil, " ", "\0", "\t", "\n", "\r", "\\"]) ).to eq("NULL # \0 \t #\n #\r #\\\n".gsub("#", "\\"))
|
981
|
-
end
|
982
|
-
end
|
983
|
-
end
|
984
|
-
end
|
985
|
-
|
986
|
-
describe PG::TextDecoder::CopyRow do
|
987
|
-
context "with default typemap" do
|
988
|
-
let!(:decoder) do
|
989
|
-
PG::TextDecoder::CopyRow.new
|
990
|
-
end
|
991
|
-
|
992
|
-
describe '#decode' do
|
993
|
-
it "should decode COPY text format to array of strings" do
|
994
|
-
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"] )
|
995
|
-
end
|
996
|
-
|
997
|
-
it 'should respect input character encoding' do
|
998
|
-
v = decoder.decode("Héllo\n".encode("iso-8859-1")).first
|
999
|
-
expect( v.encoding ).to eq(Encoding::ISO_8859_1)
|
1000
|
-
expect( v ).to eq("Héllo".encode("iso-8859-1"))
|
1001
|
-
end
|
1002
|
-
end
|
1003
|
-
end
|
1004
|
-
|
1005
|
-
context "with TypeMapByColumn" do
|
1006
|
-
let!(:tm) do
|
1007
|
-
PG::TypeMapByColumn.new [textdec_int, textdec_string, intdec_incrementer, nil]
|
1008
|
-
end
|
1009
|
-
let!(:decoder) do
|
1010
|
-
PG::TextDecoder::CopyRow.new type_map: tm
|
1011
|
-
end
|
1012
|
-
|
1013
|
-
describe '#decode' do
|
1014
|
-
it "should decode different types of Ruby objects" do
|
1015
|
-
expect( decoder.decode("123\t \0#\t#\n#\r#\\ \t234\t#\x01#\002\n".gsub("#", "\\"))).to eq( [123, " \0\t\n\r\\ ", 235, "\x01\x02"] )
|
1016
|
-
end
|
1017
|
-
end
|
1018
|
-
end
|
1019
|
-
end
|
1020
|
-
end
|
1021
|
-
|
1022
|
-
describe PG::RecordCoder do
|
1023
|
-
describe PG::TextEncoder::Record do
|
1024
|
-
context "with default typemap" do
|
1025
|
-
let!(:encoder) do
|
1026
|
-
PG::TextEncoder::Record.new
|
1027
|
-
end
|
1028
|
-
|
1029
|
-
it "should encode different types of Ruby objects" do
|
1030
|
-
expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
|
1031
|
-
to eq('("xyz","123","2456","34567","456789","5678901","[1, 2, 3]","12.1","abcdefg",)')
|
1032
|
-
end
|
1033
|
-
|
1034
|
-
it 'should output a string with correct character encoding' do
|
1035
|
-
v = encoder.encode(["Héllo"], "iso-8859-1")
|
1036
|
-
expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
|
1037
|
-
expect( v ).to eq( '("Héllo")'.encode(Encoding::ISO_8859_1) )
|
1038
|
-
end
|
1039
|
-
end
|
1040
|
-
|
1041
|
-
context "with TypeMapByClass" do
|
1042
|
-
let!(:tm) do
|
1043
|
-
tm = PG::TypeMapByClass.new
|
1044
|
-
tm[Integer] = textenc_int
|
1045
|
-
tm[Float] = intenc_incrementer
|
1046
|
-
tm[Array] = PG::TextEncoder::Array.new elements_type: textenc_string
|
1047
|
-
tm
|
1048
|
-
end
|
1049
|
-
let!(:encoder) do
|
1050
|
-
PG::TextEncoder::Record.new type_map: tm
|
1051
|
-
end
|
1052
|
-
|
1053
|
-
it "should have reasonable default values" do
|
1054
|
-
expect( encoder.name ).to be_nil
|
1055
|
-
end
|
1056
|
-
|
1057
|
-
it "copies all attributes with #dup" do
|
1058
|
-
encoder.name = "test"
|
1059
|
-
encoder.type_map = PG::TypeMapByColumn.new []
|
1060
|
-
encoder2 = encoder.dup
|
1061
|
-
expect( encoder.object_id ).to_not eq( encoder2.object_id )
|
1062
|
-
expect( encoder2.name ).to eq( "test" )
|
1063
|
-
expect( encoder2.type_map ).to be_a_kind_of( PG::TypeMapByColumn )
|
1064
|
-
end
|
1065
|
-
|
1066
|
-
describe '#encode' do
|
1067
|
-
it "should encode different types of Ruby objects" do
|
1068
|
-
expect( encoder.encode([]) ).to eq("()")
|
1069
|
-
expect( encoder.encode(["a"]) ).to eq('("a")')
|
1070
|
-
expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
|
1071
|
-
to eq('("xyz","123","2456","34567","456789","5678901","{1,2,3}","13 ","abcdefg",)')
|
1072
|
-
end
|
1073
|
-
|
1074
|
-
it "should escape special characters" do
|
1075
|
-
expect( encoder.encode([" \"\t\n\\\r"]) ).to eq("(\" \"\"\t\n##\r\")".gsub("#", "\\"))
|
1076
|
-
end
|
1077
|
-
end
|
1078
|
-
end
|
1079
|
-
end
|
1080
|
-
|
1081
|
-
describe PG::TextDecoder::Record do
|
1082
|
-
context "with default typemap" do
|
1083
|
-
let!(:decoder) do
|
1084
|
-
PG::TextDecoder::Record.new
|
1085
|
-
end
|
1086
|
-
|
1087
|
-
describe '#decode' do
|
1088
|
-
it "should decode composite text format to array of strings" do
|
1089
|
-
expect( decoder.decode('("fuzzy dice",,"",42,)') ).to eq( ["fuzzy dice",nil, "", "42", nil] )
|
1090
|
-
end
|
1091
|
-
|
1092
|
-
it 'should respect input character encoding' do
|
1093
|
-
v = decoder.decode("(Héllo)".encode("iso-8859-1")).first
|
1094
|
-
expect( v.encoding ).to eq(Encoding::ISO_8859_1)
|
1095
|
-
expect( v ).to eq("Héllo".encode("iso-8859-1"))
|
1096
|
-
end
|
1097
|
-
|
1098
|
-
it 'should raise an error on malformed input' do
|
1099
|
-
expect{ decoder.decode('') }.to raise_error(ArgumentError, /"" - Missing left parenthesis/)
|
1100
|
-
expect{ decoder.decode('(') }.to raise_error(ArgumentError, /"\(" - Unexpected end of input/)
|
1101
|
-
expect{ decoder.decode('(\\') }.to raise_error(ArgumentError, /"\(\\" - Unexpected end of input/)
|
1102
|
-
expect{ decoder.decode('()x') }.to raise_error(ArgumentError, /"\(\)x" - Junk after right parenthesis/)
|
1103
|
-
end
|
1104
|
-
end
|
1105
|
-
end
|
1106
|
-
|
1107
|
-
context "with TypeMapByColumn" do
|
1108
|
-
let!(:tm) do
|
1109
|
-
PG::TypeMapByColumn.new [textdec_int, textdec_string, intdec_incrementer, nil]
|
1110
|
-
end
|
1111
|
-
let!(:decoder) do
|
1112
|
-
PG::TextDecoder::Record.new type_map: tm
|
1113
|
-
end
|
1114
|
-
|
1115
|
-
describe '#decode' do
|
1116
|
-
it "should decode different types of Ruby objects" do
|
1117
|
-
expect( decoder.decode("(123,\" #,#\n#\r#\\ \",234,#\x01#\002)".gsub("#", "\\"))).to eq( [123, " ,\n\r\\ ", 235, "\x01\x02"] )
|
1118
|
-
end
|
1119
|
-
end
|
1120
|
-
end
|
1121
|
-
end
|
1122
|
-
end
|
1123
|
-
end
|