pg 0.18.3 → 1.4.3

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 (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