pg 0.18.4 → 1.5.3

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