pg 0.18.3 → 1.4.3

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