pg 1.0.0 → 1.5.9

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 (126) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/Gemfile +20 -0
  4. data/History.md +932 -0
  5. data/Manifest.txt +8 -3
  6. data/README-Windows.rdoc +4 -4
  7. data/README.ja.md +300 -0
  8. data/README.md +286 -0
  9. data/Rakefile +41 -138
  10. data/Rakefile.cross +71 -66
  11. data/certs/ged.pem +24 -0
  12. data/certs/kanis@comcard.de.pem +20 -0
  13. data/certs/larskanis-2022.pem +26 -0
  14. data/certs/larskanis-2023.pem +24 -0
  15. data/certs/larskanis-2024.pem +24 -0
  16. data/ext/errorcodes.def +84 -5
  17. data/ext/errorcodes.rb +1 -1
  18. data/ext/errorcodes.txt +23 -6
  19. data/ext/extconf.rb +109 -25
  20. data/ext/gvl_wrappers.c +4 -0
  21. data/ext/gvl_wrappers.h +23 -0
  22. data/ext/pg.c +213 -155
  23. data/ext/pg.h +89 -23
  24. data/ext/pg_binary_decoder.c +164 -16
  25. data/ext/pg_binary_encoder.c +238 -13
  26. data/ext/pg_coder.c +159 -35
  27. data/ext/pg_connection.c +1584 -967
  28. data/ext/pg_copy_coder.c +373 -43
  29. data/ext/pg_errors.c +1 -1
  30. data/ext/pg_record_coder.c +522 -0
  31. data/ext/pg_result.c +710 -217
  32. data/ext/pg_text_decoder.c +630 -43
  33. data/ext/pg_text_encoder.c +222 -72
  34. data/ext/pg_tuple.c +572 -0
  35. data/ext/pg_type_map.c +45 -11
  36. data/ext/pg_type_map_all_strings.c +21 -7
  37. data/ext/pg_type_map_by_class.c +59 -27
  38. data/ext/pg_type_map_by_column.c +80 -37
  39. data/ext/pg_type_map_by_mri_type.c +49 -20
  40. data/ext/pg_type_map_by_oid.c +62 -29
  41. data/ext/pg_type_map_in_ruby.c +56 -22
  42. data/ext/{util.c → pg_util.c} +12 -12
  43. data/ext/{util.h → pg_util.h} +2 -2
  44. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  45. data/lib/pg/basic_type_map_for_queries.rb +202 -0
  46. data/lib/pg/basic_type_map_for_results.rb +104 -0
  47. data/lib/pg/basic_type_registry.rb +311 -0
  48. data/lib/pg/binary_decoder/date.rb +9 -0
  49. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  50. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  51. data/lib/pg/coder.rb +36 -13
  52. data/lib/pg/connection.rb +769 -70
  53. data/lib/pg/exceptions.rb +22 -2
  54. data/lib/pg/result.rb +14 -2
  55. data/lib/pg/text_decoder/date.rb +21 -0
  56. data/lib/pg/text_decoder/inet.rb +9 -0
  57. data/lib/pg/text_decoder/json.rb +17 -0
  58. data/lib/pg/text_decoder/numeric.rb +9 -0
  59. data/lib/pg/text_decoder/timestamp.rb +30 -0
  60. data/lib/pg/text_encoder/date.rb +13 -0
  61. data/lib/pg/text_encoder/inet.rb +31 -0
  62. data/lib/pg/text_encoder/json.rb +17 -0
  63. data/lib/pg/text_encoder/numeric.rb +9 -0
  64. data/lib/pg/text_encoder/timestamp.rb +24 -0
  65. data/lib/pg/tuple.rb +30 -0
  66. data/lib/pg/type_map_by_column.rb +3 -2
  67. data/lib/pg/version.rb +4 -0
  68. data/lib/pg.rb +106 -39
  69. data/misc/openssl-pg-segfault.rb +31 -0
  70. data/misc/postgres/History.txt +9 -0
  71. data/misc/postgres/Manifest.txt +5 -0
  72. data/misc/postgres/README.txt +21 -0
  73. data/misc/postgres/Rakefile +21 -0
  74. data/misc/postgres/lib/postgres.rb +16 -0
  75. data/misc/ruby-pg/History.txt +9 -0
  76. data/misc/ruby-pg/Manifest.txt +5 -0
  77. data/misc/ruby-pg/README.txt +21 -0
  78. data/misc/ruby-pg/Rakefile +21 -0
  79. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  80. data/pg.gemspec +36 -0
  81. data/rakelib/task_extension.rb +46 -0
  82. data/sample/array_insert.rb +20 -0
  83. data/sample/async_api.rb +102 -0
  84. data/sample/async_copyto.rb +39 -0
  85. data/sample/async_mixed.rb +56 -0
  86. data/sample/check_conn.rb +21 -0
  87. data/sample/copydata.rb +71 -0
  88. data/sample/copyfrom.rb +81 -0
  89. data/sample/copyto.rb +19 -0
  90. data/sample/cursor.rb +21 -0
  91. data/sample/disk_usage_report.rb +177 -0
  92. data/sample/issue-119.rb +94 -0
  93. data/sample/losample.rb +69 -0
  94. data/sample/minimal-testcase.rb +17 -0
  95. data/sample/notify_wait.rb +72 -0
  96. data/sample/pg_statistics.rb +285 -0
  97. data/sample/replication_monitor.rb +222 -0
  98. data/sample/test_binary_values.rb +33 -0
  99. data/sample/wal_shipper.rb +434 -0
  100. data/sample/warehouse_partitions.rb +311 -0
  101. data.tar.gz.sig +0 -0
  102. metadata +138 -223
  103. metadata.gz.sig +0 -0
  104. data/.gemtest +0 -0
  105. data/ChangeLog +0 -6595
  106. data/History.rdoc +0 -422
  107. data/README.ja.rdoc +0 -14
  108. data/README.rdoc +0 -167
  109. data/lib/pg/basic_type_mapping.rb +0 -426
  110. data/lib/pg/constants.rb +0 -11
  111. data/lib/pg/text_decoder.rb +0 -51
  112. data/lib/pg/text_encoder.rb +0 -35
  113. data/spec/data/expected_trace.out +0 -26
  114. data/spec/data/random_binary_data +0 -0
  115. data/spec/helpers.rb +0 -348
  116. data/spec/pg/basic_type_mapping_spec.rb +0 -305
  117. data/spec/pg/connection_spec.rb +0 -1719
  118. data/spec/pg/result_spec.rb +0 -456
  119. data/spec/pg/type_map_by_class_spec.rb +0 -138
  120. data/spec/pg/type_map_by_column_spec.rb +0 -222
  121. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  122. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  123. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  124. data/spec/pg/type_map_spec.rb +0 -22
  125. data/spec/pg/type_spec.rb +0 -777
  126. 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