pg 1.0.0 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +42 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +141 -0
  7. data/.gitignore +22 -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.md +884 -0
  16. data/Manifest.txt +8 -3
  17. data/README-Windows.rdoc +4 -4
  18. data/README.ja.md +300 -0
  19. data/README.md +286 -0
  20. data/Rakefile +38 -138
  21. data/Rakefile.cross +63 -63
  22. data/certs/ged.pem +24 -0
  23. data/certs/larskanis-2022.pem +26 -0
  24. data/certs/larskanis-2023.pem +24 -0
  25. data/ext/errorcodes.def +80 -0
  26. data/ext/errorcodes.rb +1 -1
  27. data/ext/errorcodes.txt +22 -2
  28. data/ext/extconf.rb +106 -25
  29. data/ext/gvl_wrappers.c +4 -0
  30. data/ext/gvl_wrappers.h +23 -0
  31. data/ext/pg.c +213 -155
  32. data/ext/pg.h +89 -23
  33. data/ext/pg_binary_decoder.c +162 -16
  34. data/ext/pg_binary_encoder.c +238 -13
  35. data/ext/pg_coder.c +159 -35
  36. data/ext/pg_connection.c +1557 -972
  37. data/ext/pg_copy_coder.c +364 -38
  38. data/ext/pg_errors.c +1 -1
  39. data/ext/pg_record_coder.c +522 -0
  40. data/ext/pg_result.c +708 -215
  41. data/ext/pg_text_decoder.c +627 -43
  42. data/ext/pg_text_encoder.c +206 -62
  43. data/ext/pg_tuple.c +572 -0
  44. data/ext/pg_type_map.c +45 -11
  45. data/ext/pg_type_map_all_strings.c +21 -7
  46. data/ext/pg_type_map_by_class.c +59 -27
  47. data/ext/pg_type_map_by_column.c +80 -37
  48. data/ext/pg_type_map_by_mri_type.c +49 -20
  49. data/ext/pg_type_map_by_oid.c +62 -29
  50. data/ext/pg_type_map_in_ruby.c +56 -22
  51. data/ext/{util.c → pg_util.c} +12 -12
  52. data/ext/{util.h → pg_util.h} +2 -2
  53. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  54. data/lib/pg/basic_type_map_for_queries.rb +198 -0
  55. data/lib/pg/basic_type_map_for_results.rb +104 -0
  56. data/lib/pg/basic_type_registry.rb +299 -0
  57. data/lib/pg/binary_decoder/date.rb +9 -0
  58. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  59. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  60. data/lib/pg/coder.rb +36 -13
  61. data/lib/pg/connection.rb +749 -70
  62. data/lib/pg/exceptions.rb +16 -2
  63. data/lib/pg/result.rb +14 -2
  64. data/lib/pg/text_decoder/date.rb +18 -0
  65. data/lib/pg/text_decoder/inet.rb +9 -0
  66. data/lib/pg/text_decoder/json.rb +14 -0
  67. data/lib/pg/text_decoder/numeric.rb +9 -0
  68. data/lib/pg/text_decoder/timestamp.rb +30 -0
  69. data/lib/pg/text_encoder/date.rb +12 -0
  70. data/lib/pg/text_encoder/inet.rb +28 -0
  71. data/lib/pg/text_encoder/json.rb +14 -0
  72. data/lib/pg/text_encoder/numeric.rb +9 -0
  73. data/lib/pg/text_encoder/timestamp.rb +24 -0
  74. data/lib/pg/tuple.rb +30 -0
  75. data/lib/pg/type_map_by_column.rb +3 -2
  76. data/lib/pg/version.rb +4 -0
  77. data/lib/pg.rb +96 -39
  78. data/misc/openssl-pg-segfault.rb +31 -0
  79. data/misc/postgres/History.txt +9 -0
  80. data/misc/postgres/Manifest.txt +5 -0
  81. data/misc/postgres/README.txt +21 -0
  82. data/misc/postgres/Rakefile +21 -0
  83. data/misc/postgres/lib/postgres.rb +16 -0
  84. data/misc/ruby-pg/History.txt +9 -0
  85. data/misc/ruby-pg/Manifest.txt +5 -0
  86. data/misc/ruby-pg/README.txt +21 -0
  87. data/misc/ruby-pg/Rakefile +21 -0
  88. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  89. data/pg.gemspec +34 -0
  90. data/rakelib/task_extension.rb +46 -0
  91. data/sample/array_insert.rb +20 -0
  92. data/sample/async_api.rb +102 -0
  93. data/sample/async_copyto.rb +39 -0
  94. data/sample/async_mixed.rb +56 -0
  95. data/sample/check_conn.rb +21 -0
  96. data/sample/copydata.rb +71 -0
  97. data/sample/copyfrom.rb +81 -0
  98. data/sample/copyto.rb +19 -0
  99. data/sample/cursor.rb +21 -0
  100. data/sample/disk_usage_report.rb +177 -0
  101. data/sample/issue-119.rb +94 -0
  102. data/sample/losample.rb +69 -0
  103. data/sample/minimal-testcase.rb +17 -0
  104. data/sample/notify_wait.rb +72 -0
  105. data/sample/pg_statistics.rb +285 -0
  106. data/sample/replication_monitor.rb +222 -0
  107. data/sample/test_binary_values.rb +33 -0
  108. data/sample/wal_shipper.rb +434 -0
  109. data/sample/warehouse_partitions.rb +311 -0
  110. data/translation/.po4a-version +7 -0
  111. data/translation/po/all.pot +936 -0
  112. data/translation/po/ja.po +1036 -0
  113. data/translation/po4a.cfg +12 -0
  114. data.tar.gz.sig +0 -0
  115. metadata +147 -219
  116. metadata.gz.sig +0 -0
  117. data/ChangeLog +0 -6595
  118. data/History.rdoc +0 -422
  119. data/README.ja.rdoc +0 -14
  120. data/README.rdoc +0 -167
  121. data/lib/pg/basic_type_mapping.rb +0 -426
  122. data/lib/pg/constants.rb +0 -11
  123. data/lib/pg/text_decoder.rb +0 -51
  124. data/lib/pg/text_encoder.rb +0 -35
  125. data/spec/data/expected_trace.out +0 -26
  126. data/spec/data/random_binary_data +0 -0
  127. data/spec/helpers.rb +0 -348
  128. data/spec/pg/basic_type_mapping_spec.rb +0 -305
  129. data/spec/pg/connection_spec.rb +0 -1719
  130. data/spec/pg/result_spec.rb +0 -456
  131. data/spec/pg/type_map_by_class_spec.rb +0 -138
  132. data/spec/pg/type_map_by_column_spec.rb +0 -222
  133. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  134. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  135. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  136. data/spec/pg/type_map_spec.rb +0 -22
  137. data/spec/pg/type_spec.rb +0 -777
  138. data/spec/pg_spec.rb +0 -50
data/spec/pg/type_spec.rb DELETED
@@ -1,777 +0,0 @@
1
- #!/usr/bin/env rspec
2
- # encoding: utf-8
3
-
4
- require 'pg'
5
-
6
-
7
- describe "PG::Type derivations" do
8
- let!(:textenc_int) { PG::TextEncoder::Integer.new name: 'Integer', oid: 23 }
9
- let!(:textdec_int) { PG::TextDecoder::Integer.new name: 'Integer', oid: 23 }
10
- let!(:textenc_boolean) { PG::TextEncoder::Boolean.new }
11
- let!(:textdec_boolean) { PG::TextDecoder::Boolean.new }
12
- let!(:textenc_float) { PG::TextEncoder::Float.new }
13
- let!(:textdec_float) { PG::TextDecoder::Float.new }
14
- let!(:textenc_string) { PG::TextEncoder::String.new }
15
- let!(:textdec_string) { PG::TextDecoder::String.new }
16
- let!(:textenc_timestamp) { PG::TextEncoder::TimestampWithoutTimeZone.new }
17
- let!(:textdec_timestamp) { PG::TextDecoder::TimestampWithoutTimeZone.new }
18
- let!(:textenc_timestamptz) { PG::TextEncoder::TimestampWithTimeZone.new }
19
- let!(:textdec_timestamptz) { PG::TextDecoder::TimestampWithTimeZone.new }
20
- let!(:textenc_bytea) { PG::TextEncoder::Bytea.new }
21
- let!(:textdec_bytea) { PG::TextDecoder::Bytea.new }
22
- let!(:binaryenc_int2) { PG::BinaryEncoder::Int2.new }
23
- let!(:binaryenc_int4) { PG::BinaryEncoder::Int4.new }
24
- let!(:binaryenc_int8) { PG::BinaryEncoder::Int8.new }
25
- let!(:binarydec_integer) { PG::BinaryDecoder::Integer.new }
26
-
27
- let!(:intenc_incrementer) do
28
- Class.new(PG::SimpleEncoder) do
29
- def encode(value)
30
- (value.to_i + 1).to_s + " "
31
- end
32
- end.new
33
- end
34
- let!(:intdec_incrementer) do
35
- Class.new(PG::SimpleDecoder) do
36
- def decode(string, tuple=nil, field=nil)
37
- string.to_i+1
38
- end
39
- end.new
40
- end
41
-
42
- let!(:intenc_incrementer_with_encoding) do
43
- Class.new(PG::SimpleEncoder) do
44
- def encode(value, encoding)
45
- r = (value.to_i + 1).to_s + " #{encoding}"
46
- r.encode!(encoding)
47
- end
48
- end.new
49
- end
50
- let!(:intenc_incrementer_with_int_result) do
51
- Class.new(PG::SimpleEncoder) do
52
- def encode(value)
53
- value.to_i+1
54
- end
55
- end.new
56
- end
57
-
58
- it "shouldn't be possible to build a PG::Type directly" do
59
- expect{ PG::Coder.new }.to raise_error(TypeError, /cannot/)
60
- end
61
-
62
- describe PG::SimpleCoder do
63
- describe '#decode' do
64
- it "should offer decode method with tuple/field" do
65
- res = textdec_int.decode("123", 1, 1)
66
- expect( res ).to eq( 123 )
67
- end
68
-
69
- it "should offer decode method without tuple/field" do
70
- res = textdec_int.decode("234")
71
- expect( res ).to eq( 234 )
72
- end
73
-
74
- it "should decode with ruby decoder" do
75
- expect( intdec_incrementer.decode("3") ).to eq( 4 )
76
- end
77
-
78
- it "should decode integers of different lengths from text format" do
79
- 30.times do |zeros|
80
- expect( textdec_int.decode("1" + "0"*zeros) ).to eq( 10 ** zeros )
81
- expect( textdec_int.decode(zeros==0 ? "0" : "9"*zeros) ).to eq( 10 ** zeros - 1 )
82
- expect( textdec_int.decode("-1" + "0"*zeros) ).to eq( -10 ** zeros )
83
- expect( textdec_int.decode(zeros==0 ? "0" : "-" + "9"*zeros) ).to eq( -10 ** zeros + 1 )
84
- end
85
- 66.times do |bits|
86
- expect( textdec_int.decode((2 ** bits).to_s) ).to eq( 2 ** bits )
87
- expect( textdec_int.decode((2 ** bits - 1).to_s) ).to eq( 2 ** bits - 1 )
88
- expect( textdec_int.decode((-2 ** bits).to_s) ).to eq( -2 ** bits )
89
- expect( textdec_int.decode((-2 ** bits + 1).to_s) ).to eq( -2 ** bits + 1 )
90
- end
91
- end
92
-
93
- it 'decodes bytea to a binary string' do
94
- expect( textdec_bytea.decode("\\x00010203EF") ).to eq( "\x00\x01\x02\x03\xef".b )
95
- expect( textdec_bytea.decode("\\377\\000") ).to eq( "\xff\0".b )
96
- end
97
-
98
- context 'timestamps' do
99
- it 'decodes timestamps without timezone' do
100
- expect( textdec_timestamp.decode('2016-01-02 23:23:59.123456') ).
101
- to be_within(0.000001).of( Time.new(2016,01,02, 23, 23, 59.123456) )
102
- end
103
- it 'decodes timestamps with hour timezone' do
104
- expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-04') ).
105
- to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:00") )
106
- expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511+10') ).
107
- to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "+10:00") )
108
- end
109
- it 'decodes timestamps with hour:minute timezone' do
110
- expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-04:15') ).
111
- to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:15") )
112
- expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-0430') ).
113
- to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:30") )
114
- expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511+10:45') ).
115
- to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "+10:45") )
116
- end
117
- it 'decodes timestamps with hour:minute:sec timezone' do
118
- # SET TIME ZONE 'Europe/Dublin'; -- Was UTC−00:25:21 until 1916
119
- # SELECT '1900-01-01'::timestamptz;
120
- # -- "1900-01-01 00:00:00-00:25:21"
121
- expect( textdec_timestamptz.decode('1916-01-01 00:00:00-00:25:21') ).
122
- to be_within(0.000001).of( Time.new(1916, 1, 1, 0, 0, 0, "-00:25:21") )
123
- end
124
- end
125
-
126
- context 'identifier quotation' do
127
- it 'should build an array out of an quoted identifier string' do
128
- quoted_type = PG::TextDecoder::Identifier.new
129
- expect( quoted_type.decode(%["A.".".B"]) ).to eq( ["A.", ".B"] )
130
- expect( quoted_type.decode(%["'A"".""B'"]) ).to eq( ['\'A"."B\''] )
131
- end
132
-
133
- it 'should split unquoted identifier string' do
134
- quoted_type = PG::TextDecoder::Identifier.new
135
- expect( quoted_type.decode(%[a.b]) ).to eq( ['a','b'] )
136
- expect( quoted_type.decode(%[a]) ).to eq( ['a'] )
137
- end
138
-
139
- it 'should split identifier string with correct character encoding' do
140
- quoted_type = PG::TextDecoder::Identifier.new
141
- v = quoted_type.decode(%[Héllo].encode("iso-8859-1")).first
142
- expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
143
- expect( v ).to eq( %[Héllo].encode(Encoding::ISO_8859_1) )
144
- end
145
- end
146
-
147
- it "should raise when decode method is called with wrong args" do
148
- expect{ textdec_int.decode() }.to raise_error(ArgumentError)
149
- expect{ textdec_int.decode("123", 2, 3, 4) }.to raise_error(ArgumentError)
150
- expect{ textdec_int.decode(2, 3, 4) }.to raise_error(TypeError)
151
- expect( intdec_incrementer.decode(2, 3, 4) ).to eq( 3 )
152
- end
153
-
154
- it "should pass through nil values" do
155
- expect( textdec_string.decode( nil )).to be_nil
156
- expect( textdec_int.decode( nil )).to be_nil
157
- end
158
- end
159
-
160
- describe '#encode' do
161
- it "should offer encode method for text type" do
162
- res = textenc_int.encode(123)
163
- expect( res ).to eq( "123" )
164
- end
165
-
166
- it "should offer encode method for binary type" do
167
- res = binaryenc_int8.encode(123)
168
- expect( res ).to eq( [123].pack("q>") )
169
- end
170
-
171
- it "should encode integers from string to binary format" do
172
- expect( binaryenc_int2.encode(" -123 ") ).to eq( [-123].pack("s>") )
173
- expect( binaryenc_int4.encode(" -123 ") ).to eq( [-123].pack("l>") )
174
- expect( binaryenc_int8.encode(" -123 ") ).to eq( [-123].pack("q>") )
175
- expect( binaryenc_int2.encode(" 123-xyz ") ).to eq( [123].pack("s>") )
176
- expect( binaryenc_int4.encode(" 123-xyz ") ).to eq( [123].pack("l>") )
177
- expect( binaryenc_int8.encode(" 123-xyz ") ).to eq( [123].pack("q>") )
178
- end
179
-
180
- it "should encode integers of different lengths to text format" do
181
- 30.times do |zeros|
182
- expect( textenc_int.encode(10 ** zeros) ).to eq( "1" + "0"*zeros )
183
- expect( textenc_int.encode(10 ** zeros - 1) ).to eq( zeros==0 ? "0" : "9"*zeros )
184
- expect( textenc_int.encode(-10 ** zeros) ).to eq( "-1" + "0"*zeros )
185
- expect( textenc_int.encode(-10 ** zeros + 1) ).to eq( zeros==0 ? "0" : "-" + "9"*zeros )
186
- end
187
- 66.times do |bits|
188
- expect( textenc_int.encode(2 ** bits) ).to eq( (2 ** bits).to_s )
189
- expect( textenc_int.encode(2 ** bits - 1) ).to eq( (2 ** bits - 1).to_s )
190
- expect( textenc_int.encode(-2 ** bits) ).to eq( (-2 ** bits).to_s )
191
- expect( textenc_int.encode(-2 ** bits + 1) ).to eq( (-2 ** bits + 1).to_s )
192
- end
193
- end
194
-
195
- it "should encode integers from string to text format" do
196
- expect( textenc_int.encode(" -123 ") ).to eq( "-123" )
197
- expect( textenc_int.encode(" 123-xyz ") ).to eq( "123" )
198
- end
199
-
200
- it "should encode boolean values" do
201
- expect( textenc_boolean.encode(false) ).to eq( "f" )
202
- expect( textenc_boolean.encode(true) ).to eq( "t" )
203
- ["any", :other, "value", 0, 1, 2].each do |value|
204
- expect( textenc_boolean.encode(value) ).to eq( value.to_s )
205
- end
206
- end
207
-
208
- it "should encode special floats equally to Float#to_s" do
209
- expect( textenc_float.encode(Float::INFINITY) ).to eq( Float::INFINITY.to_s )
210
- expect( textenc_float.encode(-Float::INFINITY) ).to eq( (-Float::INFINITY).to_s )
211
- expect( textenc_float.encode(-Float::NAN) ).to eq( Float::NAN.to_s )
212
- end
213
-
214
- it "encodes binary string to bytea" do
215
- expect( textenc_bytea.encode("\x00\x01\x02\x03\xef".b) ).to eq( "\\x00010203ef" )
216
- end
217
-
218
- context 'identifier quotation' do
219
- it 'should quote and escape identifier' do
220
- quoted_type = PG::TextEncoder::Identifier.new
221
- expect( quoted_type.encode(['schema','table','col']) ).to eq( %["schema"."table"."col"] )
222
- expect( quoted_type.encode(['A.','.B']) ).to eq( %["A.".".B"] )
223
- expect( quoted_type.encode(%['A"."B']) ).to eq( %["'A"".""B'"] )
224
- expect( quoted_type.encode( nil ) ).to be_nil
225
- end
226
-
227
- it 'should quote identifiers with correct character encoding' do
228
- quoted_type = PG::TextEncoder::Identifier.new
229
- v = quoted_type.encode(['Héllo'], "iso-8859-1")
230
- expect( v ).to eq( %["Héllo"].encode(Encoding::ISO_8859_1) )
231
- expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
232
- end
233
-
234
- it "will raise a TypeError for invalid arguments to quote_ident" do
235
- quoted_type = PG::TextEncoder::Identifier.new
236
- expect{ quoted_type.encode( [nil] ) }.to raise_error(TypeError)
237
- expect{ quoted_type.encode( [['a']] ) }.to raise_error(TypeError)
238
- end
239
- end
240
-
241
- it "should encode with ruby encoder" do
242
- expect( intenc_incrementer.encode(3) ).to eq( "4 " )
243
- end
244
-
245
- it "should encode with ruby encoder and given character encoding" do
246
- r = intenc_incrementer_with_encoding.encode(3, Encoding::CP850)
247
- expect( r ).to eq( "4 CP850" )
248
- expect( r.encoding ).to eq( Encoding::CP850 )
249
- end
250
-
251
- it "should return when ruby encoder returns non string values" do
252
- expect( intenc_incrementer_with_int_result.encode(3) ).to eq( 4 )
253
- end
254
-
255
- it "should pass through nil values" do
256
- expect( textenc_string.encode( nil )).to be_nil
257
- expect( textenc_int.encode( nil )).to be_nil
258
- end
259
- end
260
-
261
- it "should be possible to marshal encoders" do
262
- mt = Marshal.dump(textenc_int)
263
- lt = Marshal.load(mt)
264
- expect( lt.to_h ).to eq( textenc_int.to_h )
265
- end
266
-
267
- it "should be possible to marshal decoders" do
268
- mt = Marshal.dump(textdec_int)
269
- lt = Marshal.load(mt)
270
- expect( lt.to_h ).to eq( textdec_int.to_h )
271
- end
272
-
273
- it "should respond to to_h" do
274
- expect( textenc_int.to_h ).to eq( {
275
- name: 'Integer', oid: 23, format: 0
276
- } )
277
- end
278
-
279
- it "should have reasonable default values" do
280
- t = PG::TextEncoder::String.new
281
- expect( t.format ).to eq( 0 )
282
- expect( t.oid ).to eq( 0 )
283
- expect( t.name ).to be_nil
284
-
285
- t = PG::BinaryEncoder::Int4.new
286
- expect( t.format ).to eq( 1 )
287
- expect( t.oid ).to eq( 0 )
288
- expect( t.name ).to be_nil
289
-
290
- t = PG::TextDecoder::String.new
291
- expect( t.format ).to eq( 0 )
292
- expect( t.oid ).to eq( 0 )
293
- expect( t.name ).to be_nil
294
-
295
- t = PG::BinaryDecoder::String.new
296
- expect( t.format ).to eq( 1 )
297
- expect( t.oid ).to eq( 0 )
298
- expect( t.name ).to be_nil
299
- end
300
- end
301
-
302
- describe PG::CompositeCoder do
303
- describe "Array types" do
304
- let!(:textenc_string_array) { PG::TextEncoder::Array.new elements_type: textenc_string }
305
- let!(:textdec_string_array) { PG::TextDecoder::Array.new elements_type: textdec_string }
306
- let!(:textenc_int_array) { PG::TextEncoder::Array.new elements_type: textenc_int, needs_quotation: false }
307
- let!(:textdec_int_array) { PG::TextDecoder::Array.new elements_type: textdec_int, needs_quotation: false }
308
- let!(:textenc_float_array) { PG::TextEncoder::Array.new elements_type: textenc_float, needs_quotation: false }
309
- let!(:textdec_float_array) { PG::TextDecoder::Array.new elements_type: textdec_float, needs_quotation: false }
310
- let!(:textenc_timestamp_array) { PG::TextEncoder::Array.new elements_type: textenc_timestamp, needs_quotation: false }
311
- let!(:textdec_timestamp_array) { PG::TextDecoder::Array.new elements_type: textdec_timestamp, needs_quotation: false }
312
- let!(:textenc_string_array_with_delimiter) { PG::TextEncoder::Array.new elements_type: textenc_string, delimiter: ';' }
313
- let!(:textdec_string_array_with_delimiter) { PG::TextDecoder::Array.new elements_type: textdec_string, delimiter: ';' }
314
- let!(:textdec_bytea_array) { PG::TextDecoder::Array.new elements_type: textdec_bytea }
315
-
316
- #
317
- # Array parser specs are thankfully borrowed from here:
318
- # https://github.com/dockyard/pg_array_parser
319
- #
320
- describe '#decode' do
321
- context 'one dimensional arrays' do
322
- context 'empty' do
323
- it 'returns an empty array' do
324
- expect( textdec_string_array.decode(%[{}]) ).to eq( [] )
325
- end
326
- end
327
-
328
- context 'no strings' do
329
- it 'returns an array of strings' do
330
- expect( textdec_string_array.decode(%[{1,2,3}]) ).to eq( ['1','2','3'] )
331
- end
332
- end
333
-
334
- context 'NULL values' do
335
- it 'returns an array of strings, with nils replacing NULL characters' do
336
- expect( textdec_string_array.decode(%[{1,NULL,NULL}]) ).to eq( ['1',nil,nil] )
337
- end
338
- end
339
-
340
- context 'quoted NULL' do
341
- it 'returns an array with the word NULL' do
342
- expect( textdec_string_array.decode(%[{1,"NULL",3}]) ).to eq( ['1','NULL','3'] )
343
- end
344
- end
345
-
346
- context 'strings' do
347
- it 'returns an array of strings when containing commas in a quoted string' do
348
- expect( textdec_string_array.decode(%[{1,"2,3",4}]) ).to eq( ['1','2,3','4'] )
349
- end
350
-
351
- it 'returns an array of strings when containing an escaped quote' do
352
- expect( textdec_string_array.decode(%[{1,"2\\",3",4}]) ).to eq( ['1','2",3','4'] )
353
- end
354
-
355
- it 'returns an array of strings when containing an escaped backslash' do
356
- expect( textdec_string_array.decode(%[{1,"2\\\\",3,4}]) ).to eq( ['1','2\\','3','4'] )
357
- expect( textdec_string_array.decode(%[{1,"2\\\\\\",3",4}]) ).to eq( ['1','2\\",3','4'] )
358
- end
359
-
360
- it 'returns an array containing empty strings' do
361
- expect( textdec_string_array.decode(%[{1,"",3,""}]) ).to eq( ['1', '', '3', ''] )
362
- end
363
-
364
- it 'returns an array containing unicode strings' do
365
- expect( textdec_string_array.decode(%[{"Paragraph 399(b)(i) – “valid leave” – meaning"}]) ).to eq(['Paragraph 399(b)(i) – “valid leave” – meaning'])
366
- end
367
-
368
- it 'respects a different delimiter' do
369
- expect( textdec_string_array_with_delimiter.decode(%[{1;2;3}]) ).to eq( ['1','2','3'] )
370
- end
371
- end
372
-
373
- context 'bytea' do
374
- it 'returns an array of binary strings' do
375
- expect( textdec_bytea_array.decode(%[{"\\\\x00010203EF","2,3",\\377}]) ).to eq( ["\x00\x01\x02\x03\xef".b,"2,3".b,"\xff".b] )
376
- end
377
- end
378
-
379
- end
380
-
381
- context 'two dimensional arrays' do
382
- context 'empty' do
383
- it 'returns an empty array' do
384
- expect( textdec_string_array.decode(%[{{}}]) ).to eq( [[]] )
385
- expect( textdec_string_array.decode(%[{{},{}}]) ).to eq( [[],[]] )
386
- end
387
- end
388
- context 'no strings' do
389
- it 'returns an array of strings with a sub array' do
390
- expect( textdec_string_array.decode(%[{1,{2,3},4}]) ).to eq( ['1',['2','3'],'4'] )
391
- end
392
- end
393
- context 'strings' do
394
- it 'returns an array of strings with a sub array' do
395
- expect( textdec_string_array.decode(%[{1,{"2,3"},4}]) ).to eq( ['1',['2,3'],'4'] )
396
- end
397
- it 'returns an array of strings with a sub array and a quoted }' do
398
- expect( textdec_string_array.decode(%[{1,{"2,}3",NULL},4}]) ).to eq( ['1',['2,}3',nil],'4'] )
399
- end
400
- it 'returns an array of strings with a sub array and a quoted {' do
401
- expect( textdec_string_array.decode(%[{1,{"2,{3"},4}]) ).to eq( ['1',['2,{3'],'4'] )
402
- end
403
- it 'returns an array of strings with a sub array and a quoted { and escaped quote' do
404
- expect( textdec_string_array.decode(%[{1,{"2\\",{3"},4}]) ).to eq( ['1',['2",{3'],'4'] )
405
- end
406
- it 'returns an array of strings with a sub array with empty strings' do
407
- expect( textdec_string_array.decode(%[{1,{""},4,{""}}]) ).to eq( ['1',[''],'4',['']] )
408
- end
409
- end
410
- context 'timestamps' do
411
- it 'decodes an array of timestamps with sub arrays' do
412
- expect( textdec_timestamp_array.decode('{2014-12-31 00:00:00,{NULL,2016-01-02 23:23:59.0000000}}') ).
413
- to eq( [Time.new(2014,12,31),[nil, Time.new(2016,01,02, 23, 23, 59)]] )
414
- end
415
- end
416
- end
417
- context 'three dimensional arrays' do
418
- context 'empty' do
419
- it 'returns an empty array' do
420
- expect( textdec_string_array.decode(%[{{{}}}]) ).to eq( [[[]]] )
421
- expect( textdec_string_array.decode(%[{{{},{}},{{},{}}}]) ).to eq( [[[],[]],[[],[]]] )
422
- end
423
- end
424
- it 'returns an array of strings with sub arrays' do
425
- expect( textdec_string_array.decode(%[{1,{2,{3,4}},{NULL,6},7}]) ).to eq( ['1',['2',['3','4']],[nil,'6'],'7'] )
426
- end
427
- end
428
-
429
- it 'should decode array of types with decoder in ruby space' do
430
- array_type = PG::TextDecoder::Array.new elements_type: intdec_incrementer
431
- expect( array_type.decode(%[{3,4}]) ).to eq( [4,5] )
432
- end
433
-
434
- it 'should decode array of nil types' do
435
- array_type = PG::TextDecoder::Array.new elements_type: nil
436
- expect( array_type.decode(%[{3,4}]) ).to eq( ['3','4'] )
437
- end
438
- end
439
-
440
- describe '#encode' do
441
- context 'three dimensional arrays' do
442
- it 'encodes an array of strings and numbers with sub arrays' do
443
- expect( textenc_string_array.encode(['1',['2',['3','4']],[nil,6],7.8]) ).to eq( %[{1,{2,{3,4}},{NULL,6},7.8}] )
444
- end
445
- it 'encodes an array of strings with quotes' do
446
- expect( textenc_string_array.encode(['',[' ',['{','}','\\',',','"','\t']]]) ).to eq( %[{"",{" ",{"{","}","\\\\",",","\\"","\\\\t"}}}] )
447
- end
448
- it 'encodes an array of int8 with sub arrays' do
449
- expect( textenc_int_array.encode([1,[2,[3,4]],[nil,6],7]) ).to eq( %[{1,{2,{3,4}},{NULL,6},7}] )
450
- end
451
- it 'encodes an array of int8 with strings' do
452
- expect( textenc_int_array.encode(['1',['2'],'3']) ).to eq( %[{1,{2},3}] )
453
- end
454
- it 'encodes an array of float8 with sub arrays' do
455
- expect( textenc_float_array.encode([1000.11,[-0.00221,[3.31,-441]],[nil,6.61],-7.71]) ).to match(Regexp.new(%[^{1.0001*E+*03,{-2.2*E-*03,{3.3*E+*00,-4.4*E+*02}},{NULL,6.6*E+*00},-7.7*E+*00}$].gsub(/([\.\+\{\}\,])/, "\\\\\\1").gsub(/\*/, "\\d*")))
456
- end
457
- end
458
- context 'two dimensional arrays' do
459
- it 'encodes an array of timestamps with sub arrays' do
460
- expect( textenc_timestamp_array.encode([Time.new(2014,12,31),[nil, Time.new(2016,01,02, 23, 23, 59.99)]]) ).
461
- to eq( %[{2014-12-31 00:00:00.000000000,{NULL,2016-01-02 23:23:59.990000000}}] )
462
- end
463
- end
464
- context 'one dimensional array' do
465
- it 'can encode empty arrays' do
466
- expect( textenc_int_array.encode([]) ).to eq( '{}' )
467
- expect( textenc_string_array.encode([]) ).to eq( '{}' )
468
- end
469
- it 'encodes an array of NULL strings w/wo quotes' do
470
- expect( textenc_string_array.encode(['NUL', 'NULL', 'NULLL', 'nul', 'null', 'nulll']) ).to eq( %[{NUL,"NULL",NULLL,nul,"null",nulll}] )
471
- end
472
- it 'respects a different delimiter' do
473
- expect( textenc_string_array_with_delimiter.encode(['a','b,','c']) ).to eq( '{a;b,;c}' )
474
- end
475
- end
476
-
477
- context 'array of types with encoder in ruby space' do
478
- it 'encodes with quotation and default character encoding' do
479
- array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
480
- r = array_type.encode([3,4])
481
- expect( r ).to eq( %[{"4 ","5 "}] )
482
- expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
483
- end
484
-
485
- it 'encodes with quotation and given character encoding' do
486
- array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
487
- r = array_type.encode([3,4], Encoding::CP850)
488
- expect( r ).to eq( %[{"4 ","5 "}] )
489
- expect( r.encoding ).to eq( Encoding::CP850 )
490
- end
491
-
492
- it 'encodes without quotation' do
493
- array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: false
494
- expect( array_type.encode([3,4]) ).to eq( %[{4 ,5 }] )
495
- end
496
-
497
- it 'encodes with default character encoding' do
498
- array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
499
- r = array_type.encode([3,4])
500
- expect( r ).to eq( %[{"4 ASCII-8BIT","5 ASCII-8BIT"}] )
501
- expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
502
- end
503
-
504
- it 'encodes with given character encoding' do
505
- array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
506
- r = array_type.encode([3,4], Encoding::CP850)
507
- expect( r ).to eq( %[{"4 CP850","5 CP850"}] )
508
- expect( r.encoding ).to eq( Encoding::CP850 )
509
- end
510
-
511
- it "should raise when ruby encoder returns non string values" do
512
- array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_int_result, needs_quotation: false
513
- expect{ array_type.encode([3,4]) }.to raise_error(TypeError)
514
- end
515
- end
516
-
517
- it "should pass through non Array inputs" do
518
- expect( textenc_float_array.encode("text") ).to eq( "text" )
519
- expect( textenc_float_array.encode(1234) ).to eq( "1234" )
520
- end
521
-
522
- context 'literal quotation' do
523
- it 'should quote and escape literals' do
524
- quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
525
- expect( quoted_type.encode(["'A\",","\\B'"]) ).to eq( %['{"''A\\",","\\\\B''"}'] )
526
- end
527
-
528
- it 'should quote literals with correct character encoding' do
529
- quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
530
- v = quoted_type.encode(["Héllo"], "iso-8859-1")
531
- expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
532
- expect( v ).to eq( %['{Héllo}'].encode(Encoding::ISO_8859_1) )
533
- end
534
- end
535
- end
536
-
537
- it "should be possible to marshal encoders" do
538
- mt = Marshal.dump(textenc_int_array)
539
- lt = Marshal.load(mt)
540
- expect( lt.to_h ).to eq( textenc_int_array.to_h )
541
- end
542
-
543
- it "should be possible to marshal encoders" do
544
- mt = Marshal.dump(textdec_int_array)
545
- lt = Marshal.load(mt)
546
- expect( lt.to_h ).to eq( textdec_int_array.to_h )
547
- end
548
-
549
- it "should respond to to_h" do
550
- expect( textenc_int_array.to_h ).to eq( {
551
- name: nil, oid: 0, format: 0,
552
- elements_type: textenc_int, needs_quotation: false, delimiter: ','
553
- } )
554
- end
555
-
556
- it "shouldn't accept invalid elements_types" do
557
- expect{ PG::TextEncoder::Array.new elements_type: false }.to raise_error(TypeError)
558
- end
559
-
560
- it "should have reasonable default values" do
561
- t = PG::TextEncoder::Array.new
562
- expect( t.format ).to eq( 0 )
563
- expect( t.oid ).to eq( 0 )
564
- expect( t.name ).to be_nil
565
- expect( t.needs_quotation? ).to eq( true )
566
- expect( t.delimiter ).to eq( ',' )
567
- expect( t.elements_type ).to be_nil
568
- end
569
- end
570
-
571
- it "should encode Strings as base64 in TextEncoder" do
572
- e = PG::TextEncoder::ToBase64.new
573
- expect( e.encode("") ).to eq("")
574
- expect( e.encode("x") ).to eq("eA==")
575
- expect( e.encode("xx") ).to eq("eHg=")
576
- expect( e.encode("xxx") ).to eq("eHh4")
577
- expect( e.encode("xxxx") ).to eq("eHh4eA==")
578
- expect( e.encode("xxxxx") ).to eq("eHh4eHg=")
579
- expect( e.encode("\0\n\t") ).to eq("AAoJ")
580
- expect( e.encode("(\xFBm") ).to eq("KPtt")
581
- end
582
-
583
- it 'should encode Strings as base64 with correct character encoding' do
584
- e = PG::TextEncoder::ToBase64.new
585
- v = e.encode("Héllo".encode("utf-16le"), "iso-8859-1")
586
- expect( v ).to eq("SOlsbG8=")
587
- expect( v.encoding ).to eq(Encoding::ISO_8859_1)
588
- end
589
-
590
- it "should encode Strings as base64 in BinaryDecoder" do
591
- e = PG::BinaryDecoder::ToBase64.new
592
- expect( e.decode("x") ).to eq("eA==")
593
- v = e.decode("Héllo".encode("utf-16le"))
594
- expect( v ).to eq("SADpAGwAbABvAA==")
595
- expect( v.encoding ).to eq(Encoding::ASCII_8BIT)
596
- end
597
-
598
- it "should encode Integers as base64" do
599
- # Not really useful, but ensures that two-pass element and composite element encoders work.
600
- e = PG::TextEncoder::ToBase64.new( elements_type: PG::TextEncoder::Array.new( elements_type: PG::TextEncoder::Integer.new, needs_quotation: false ))
601
- expect( e.encode([1]) ).to eq(["{1}"].pack("m").chomp)
602
- expect( e.encode([12]) ).to eq(["{12}"].pack("m").chomp)
603
- expect( e.encode([123]) ).to eq(["{123}"].pack("m").chomp)
604
- expect( e.encode([1234]) ).to eq(["{1234}"].pack("m").chomp)
605
- expect( e.encode([12345]) ).to eq(["{12345}"].pack("m").chomp)
606
- expect( e.encode([123456]) ).to eq(["{123456}"].pack("m").chomp)
607
- expect( e.encode([1234567]) ).to eq(["{1234567}"].pack("m").chomp)
608
- end
609
-
610
- it "should decode base64 to Strings in TextDecoder" do
611
- e = PG::TextDecoder::FromBase64.new
612
- expect( e.decode("") ).to eq("")
613
- expect( e.decode("eA==") ).to eq("x")
614
- expect( e.decode("eHg=") ).to eq("xx")
615
- expect( e.decode("eHh4") ).to eq("xxx")
616
- expect( e.decode("eHh4eA==") ).to eq("xxxx")
617
- expect( e.decode("eHh4eHg=") ).to eq("xxxxx")
618
- expect( e.decode("AAoJ") ).to eq("\0\n\t")
619
- expect( e.decode("KPtt") ).to eq("(\xFBm")
620
- end
621
-
622
- it "should decode base64 in BinaryEncoder" do
623
- e = PG::BinaryEncoder::FromBase64.new
624
- expect( e.encode("eA==") ).to eq("x")
625
-
626
- e = PG::BinaryEncoder::FromBase64.new( elements_type: PG::TextEncoder::Integer.new )
627
- expect( e.encode(124) ).to eq("124=".unpack("m")[0])
628
- end
629
-
630
- it "should decode base64 to Integers" do
631
- # Not really useful, but ensures that composite element encoders work.
632
- e = PG::TextDecoder::FromBase64.new( elements_type: PG::TextDecoder::Array.new( elements_type: PG::TextDecoder::Integer.new ))
633
- expect( e.decode(["{1}"].pack("m")) ).to eq([1])
634
- expect( e.decode(["{12}"].pack("m")) ).to eq([12])
635
- expect( e.decode(["{123}"].pack("m")) ).to eq([123])
636
- expect( e.decode(["{1234}"].pack("m")) ).to eq([1234])
637
- expect( e.decode(["{12345}"].pack("m")) ).to eq([12345])
638
- expect( e.decode(["{123456}"].pack("m")) ).to eq([123456])
639
- expect( e.decode(["{1234567}"].pack("m")) ).to eq([1234567])
640
- expect( e.decode(["{12345678}"].pack("m")) ).to eq([12345678])
641
-
642
- e = PG::TextDecoder::FromBase64.new( elements_type: PG::BinaryDecoder::Integer.new )
643
- expect( e.decode("ALxhTg==") ).to eq(12345678)
644
- end
645
-
646
- it "should decode base64 with garbage" do
647
- e = PG::TextDecoder::FromBase64.new format: 1
648
- expect( e.decode("=") ).to eq("=".unpack("m")[0])
649
- expect( e.decode("==") ).to eq("==".unpack("m")[0])
650
- expect( e.decode("===") ).to eq("===".unpack("m")[0])
651
- expect( e.decode("====") ).to eq("====".unpack("m")[0])
652
- expect( e.decode("a=") ).to eq("a=".unpack("m")[0])
653
- expect( e.decode("a==") ).to eq("a==".unpack("m")[0])
654
- expect( e.decode("a===") ).to eq("a===".unpack("m")[0])
655
- expect( e.decode("a====") ).to eq("a====".unpack("m")[0])
656
- expect( e.decode("aa=") ).to eq("aa=".unpack("m")[0])
657
- expect( e.decode("aa==") ).to eq("aa==".unpack("m")[0])
658
- expect( e.decode("aa===") ).to eq("aa===".unpack("m")[0])
659
- expect( e.decode("aa====") ).to eq("aa====".unpack("m")[0])
660
- expect( e.decode("aaa=") ).to eq("aaa=".unpack("m")[0])
661
- expect( e.decode("aaa==") ).to eq("aaa==".unpack("m")[0])
662
- expect( e.decode("aaa===") ).to eq("aaa===".unpack("m")[0])
663
- expect( e.decode("aaa====") ).to eq("aaa====".unpack("m")[0])
664
- expect( e.decode("=aa") ).to eq("=aa=".unpack("m")[0])
665
- expect( e.decode("=aa=") ).to eq("=aa=".unpack("m")[0])
666
- expect( e.decode("=aa==") ).to eq("=aa==".unpack("m")[0])
667
- expect( e.decode("=aa===") ).to eq("=aa===".unpack("m")[0])
668
- end
669
- end
670
-
671
- describe PG::CopyCoder do
672
- describe PG::TextEncoder::CopyRow do
673
- context "with default typemap" do
674
- let!(:encoder) do
675
- PG::TextEncoder::CopyRow.new
676
- end
677
-
678
- it "should encode different types of Ruby objects" do
679
- expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
680
- to eq("xyz\t123\t2456\t34567\t456789\t5678901\t[1, 2, 3]\t12.1\tabcdefg\t\\N\n")
681
- end
682
-
683
- it 'should output a string with correct character encoding' do
684
- v = encoder.encode(["Héllo"], "iso-8859-1")
685
- expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
686
- expect( v ).to eq( "Héllo\n".encode(Encoding::ISO_8859_1) )
687
- end
688
- end
689
-
690
- context "with TypeMapByClass" do
691
- let!(:tm) do
692
- tm = PG::TypeMapByClass.new
693
- tm[Integer] = textenc_int
694
- tm[Float] = intenc_incrementer
695
- tm[Array] = PG::TextEncoder::Array.new elements_type: textenc_string
696
- tm
697
- end
698
- let!(:encoder) do
699
- PG::TextEncoder::CopyRow.new type_map: tm
700
- end
701
-
702
- it "should have reasonable default values" do
703
- expect( encoder.name ).to be_nil
704
- expect( encoder.delimiter ).to eq( "\t" )
705
- expect( encoder.null_string ).to eq( "\\N" )
706
- end
707
-
708
- it "copies all attributes with #dup" do
709
- encoder.name = "test"
710
- encoder.delimiter = "#"
711
- encoder.null_string = "NULL"
712
- encoder.type_map = PG::TypeMapByColumn.new []
713
- encoder2 = encoder.dup
714
- expect( encoder.object_id ).to_not eq( encoder2.object_id )
715
- expect( encoder2.name ).to eq( "test" )
716
- expect( encoder2.delimiter ).to eq( "#" )
717
- expect( encoder2.null_string ).to eq( "NULL" )
718
- expect( encoder2.type_map ).to be_a_kind_of( PG::TypeMapByColumn )
719
- end
720
-
721
- describe '#encode' do
722
- it "should encode different types of Ruby objects" do
723
- expect( encoder.encode([]) ).to eq("\n")
724
- expect( encoder.encode(["a"]) ).to eq("a\n")
725
- expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
726
- to eq("xyz\t123\t2456\t34567\t456789\t5678901\t{1,2,3}\t13 \tabcdefg\t\\N\n")
727
- end
728
-
729
- it "should escape special characters" do
730
- expect( encoder.encode([" \0\t\n\r\\"]) ).to eq(" \0#\t#\n#\r#\\\n".gsub("#", "\\"))
731
- end
732
-
733
- it "should escape with different delimiter" do
734
- encoder.delimiter = " "
735
- encoder.null_string = "NULL"
736
- expect( encoder.encode([nil, " ", "\0", "\t", "\n", "\r", "\\"]) ).to eq("NULL # \0 \t #\n #\r #\\\n".gsub("#", "\\"))
737
- end
738
- end
739
- end
740
- end
741
-
742
- describe PG::TextDecoder::CopyRow do
743
- context "with default typemap" do
744
- let!(:decoder) do
745
- PG::TextDecoder::CopyRow.new
746
- end
747
-
748
- describe '#decode' do
749
- it "should decode different types of Ruby objects" do
750
- 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"] )
751
- end
752
-
753
- it 'should respect input character encoding' do
754
- v = decoder.decode("Héllo\n".encode("iso-8859-1")).first
755
- expect( v.encoding ).to eq(Encoding::ISO_8859_1)
756
- expect( v ).to eq("Héllo".encode("iso-8859-1"))
757
- end
758
- end
759
- end
760
-
761
- context "with TypeMapByColumn" do
762
- let!(:tm) do
763
- PG::TypeMapByColumn.new [textdec_int, textdec_string, intdec_incrementer, nil]
764
- end
765
- let!(:decoder) do
766
- PG::TextDecoder::CopyRow.new type_map: tm
767
- end
768
-
769
- describe '#decode' do
770
- it "should decode different types of Ruby objects" do
771
- 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"] )
772
- end
773
- end
774
- end
775
- end
776
- end
777
- end