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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +36 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +86 -0
  6. data/.github/workflows/source-gem.yml +130 -0
  7. data/.gitignore +13 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +14 -0
  15. data/History.rdoc +95 -7
  16. data/Manifest.txt +0 -1
  17. data/README.rdoc +7 -6
  18. data/Rakefile +27 -138
  19. data/Rakefile.cross +6 -5
  20. data/certs/ged.pem +24 -0
  21. data/ext/errorcodes.def +8 -0
  22. data/ext/errorcodes.txt +3 -1
  23. data/ext/extconf.rb +91 -19
  24. data/ext/gvl_wrappers.c +4 -0
  25. data/ext/gvl_wrappers.h +23 -0
  26. data/ext/pg.c +59 -4
  27. data/ext/pg.h +18 -1
  28. data/ext/pg_coder.c +82 -28
  29. data/ext/pg_connection.c +673 -493
  30. data/ext/pg_copy_coder.c +45 -16
  31. data/ext/pg_record_coder.c +39 -11
  32. data/ext/pg_result.c +61 -31
  33. data/ext/pg_text_decoder.c +1 -1
  34. data/ext/pg_text_encoder.c +6 -6
  35. data/ext/pg_tuple.c +47 -21
  36. data/ext/pg_type_map.c +41 -8
  37. data/ext/pg_type_map_all_strings.c +14 -1
  38. data/ext/pg_type_map_by_class.c +49 -24
  39. data/ext/pg_type_map_by_column.c +64 -28
  40. data/ext/pg_type_map_by_mri_type.c +47 -18
  41. data/ext/pg_type_map_by_oid.c +52 -23
  42. data/ext/pg_type_map_in_ruby.c +50 -19
  43. data/ext/pg_util.c +2 -2
  44. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  45. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  46. data/lib/pg/basic_type_map_for_results.rb +81 -0
  47. data/lib/pg/basic_type_registry.rb +296 -0
  48. data/lib/pg/coder.rb +1 -1
  49. data/lib/pg/connection.rb +588 -58
  50. data/lib/pg/version.rb +4 -0
  51. data/lib/pg.rb +47 -32
  52. data/misc/openssl-pg-segfault.rb +31 -0
  53. data/misc/postgres/History.txt +9 -0
  54. data/misc/postgres/Manifest.txt +5 -0
  55. data/misc/postgres/README.txt +21 -0
  56. data/misc/postgres/Rakefile +21 -0
  57. data/misc/postgres/lib/postgres.rb +16 -0
  58. data/misc/ruby-pg/History.txt +9 -0
  59. data/misc/ruby-pg/Manifest.txt +5 -0
  60. data/misc/ruby-pg/README.txt +21 -0
  61. data/misc/ruby-pg/Rakefile +21 -0
  62. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  63. data/pg.gemspec +32 -0
  64. data/sample/array_insert.rb +20 -0
  65. data/sample/async_api.rb +106 -0
  66. data/sample/async_copyto.rb +39 -0
  67. data/sample/async_mixed.rb +56 -0
  68. data/sample/check_conn.rb +21 -0
  69. data/sample/copydata.rb +71 -0
  70. data/sample/copyfrom.rb +81 -0
  71. data/sample/copyto.rb +19 -0
  72. data/sample/cursor.rb +21 -0
  73. data/sample/disk_usage_report.rb +177 -0
  74. data/sample/issue-119.rb +94 -0
  75. data/sample/losample.rb +69 -0
  76. data/sample/minimal-testcase.rb +17 -0
  77. data/sample/notify_wait.rb +72 -0
  78. data/sample/pg_statistics.rb +285 -0
  79. data/sample/replication_monitor.rb +222 -0
  80. data/sample/test_binary_values.rb +33 -0
  81. data/sample/wal_shipper.rb +434 -0
  82. data/sample/warehouse_partitions.rb +311 -0
  83. data.tar.gz.sig +0 -0
  84. metadata +76 -217
  85. metadata.gz.sig +0 -0
  86. data/ChangeLog +0 -0
  87. data/lib/pg/basic_type_mapping.rb +0 -522
  88. data/spec/data/expected_trace.out +0 -26
  89. data/spec/data/random_binary_data +0 -0
  90. data/spec/helpers.rb +0 -380
  91. data/spec/pg/basic_type_mapping_spec.rb +0 -630
  92. data/spec/pg/connection_spec.rb +0 -1949
  93. data/spec/pg/connection_sync_spec.rb +0 -41
  94. data/spec/pg/result_spec.rb +0 -681
  95. data/spec/pg/tuple_spec.rb +0 -333
  96. data/spec/pg/type_map_by_class_spec.rb +0 -138
  97. data/spec/pg/type_map_by_column_spec.rb +0 -226
  98. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  99. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  100. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  101. data/spec/pg/type_map_spec.rb +0 -22
  102. data/spec/pg/type_spec.rb +0 -1123
  103. 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