pg 0.18.2 → 1.4.5

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 (119) 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 +131 -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 +480 -4
  17. data/Manifest.txt +8 -21
  18. data/README-Windows.rdoc +17 -28
  19. data/README.ja.rdoc +1 -2
  20. data/README.rdoc +92 -20
  21. data/Rakefile +33 -133
  22. data/Rakefile.cross +89 -67
  23. data/certs/ged.pem +24 -0
  24. data/certs/larskanis-2022.pem +26 -0
  25. data/ext/errorcodes.def +113 -0
  26. data/ext/errorcodes.rb +1 -1
  27. data/ext/errorcodes.txt +36 -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 +216 -172
  32. data/ext/pg.h +93 -98
  33. data/ext/pg_binary_decoder.c +85 -16
  34. data/ext/pg_binary_encoder.c +25 -22
  35. data/ext/pg_coder.c +176 -40
  36. data/ext/pg_connection.c +1735 -1138
  37. data/ext/pg_copy_coder.c +95 -28
  38. data/ext/pg_errors.c +1 -1
  39. data/ext/pg_record_coder.c +521 -0
  40. data/ext/pg_result.c +642 -221
  41. data/ext/pg_text_decoder.c +609 -41
  42. data/ext/pg_text_encoder.c +254 -100
  43. data/ext/pg_tuple.c +569 -0
  44. data/ext/pg_type_map.c +62 -22
  45. data/ext/pg_type_map_all_strings.c +20 -6
  46. data/ext/pg_type_map_by_class.c +55 -25
  47. data/ext/pg_type_map_by_column.c +81 -42
  48. data/ext/pg_type_map_by_mri_type.c +49 -20
  49. data/ext/pg_type_map_by_oid.c +56 -26
  50. data/ext/pg_type_map_in_ruby.c +52 -21
  51. data/ext/{util.c → pg_util.c} +12 -12
  52. data/ext/{util.h → pg_util.h} +2 -2
  53. data/lib/pg/basic_type_map_based_on_result.rb +47 -0
  54. data/lib/pg/basic_type_map_for_queries.rb +193 -0
  55. data/lib/pg/basic_type_map_for_results.rb +81 -0
  56. data/lib/pg/basic_type_registry.rb +301 -0
  57. data/lib/pg/binary_decoder.rb +23 -0
  58. data/lib/pg/coder.rb +24 -3
  59. data/lib/pg/connection.rb +711 -64
  60. data/lib/pg/constants.rb +2 -1
  61. data/lib/pg/exceptions.rb +9 -2
  62. data/lib/pg/result.rb +24 -7
  63. data/lib/pg/text_decoder.rb +27 -23
  64. data/lib/pg/text_encoder.rb +40 -8
  65. data/lib/pg/tuple.rb +30 -0
  66. data/lib/pg/type_map_by_column.rb +3 -2
  67. data/lib/pg/version.rb +4 -0
  68. data/lib/pg.rb +61 -36
  69. data/misc/openssl-pg-segfault.rb +31 -0
  70. data/misc/postgres/History.txt +9 -0
  71. data/misc/postgres/Manifest.txt +5 -0
  72. data/misc/postgres/README.txt +21 -0
  73. data/misc/postgres/Rakefile +21 -0
  74. data/misc/postgres/lib/postgres.rb +16 -0
  75. data/misc/ruby-pg/History.txt +9 -0
  76. data/misc/ruby-pg/Manifest.txt +5 -0
  77. data/misc/ruby-pg/README.txt +21 -0
  78. data/misc/ruby-pg/Rakefile +21 -0
  79. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  80. data/pg.gemspec +32 -0
  81. data/rakelib/task_extension.rb +46 -0
  82. data/sample/array_insert.rb +1 -1
  83. data/sample/async_api.rb +4 -8
  84. data/sample/async_copyto.rb +1 -1
  85. data/sample/async_mixed.rb +1 -1
  86. data/sample/check_conn.rb +1 -1
  87. data/sample/copydata.rb +71 -0
  88. data/sample/copyfrom.rb +1 -1
  89. data/sample/copyto.rb +1 -1
  90. data/sample/cursor.rb +1 -1
  91. data/sample/disk_usage_report.rb +6 -15
  92. data/sample/issue-119.rb +2 -2
  93. data/sample/losample.rb +1 -1
  94. data/sample/minimal-testcase.rb +2 -2
  95. data/sample/notify_wait.rb +1 -1
  96. data/sample/pg_statistics.rb +6 -15
  97. data/sample/replication_monitor.rb +9 -18
  98. data/sample/test_binary_values.rb +1 -1
  99. data/sample/wal_shipper.rb +2 -2
  100. data/sample/warehouse_partitions.rb +8 -17
  101. data.tar.gz.sig +0 -0
  102. metadata +74 -216
  103. metadata.gz.sig +0 -0
  104. data/ChangeLog +0 -5545
  105. data/lib/pg/basic_type_mapping.rb +0 -399
  106. data/spec/data/expected_trace.out +0 -26
  107. data/spec/data/random_binary_data +0 -0
  108. data/spec/helpers.rb +0 -355
  109. data/spec/pg/basic_type_mapping_spec.rb +0 -251
  110. data/spec/pg/connection_spec.rb +0 -1535
  111. data/spec/pg/result_spec.rb +0 -449
  112. data/spec/pg/type_map_by_class_spec.rb +0 -138
  113. data/spec/pg/type_map_by_column_spec.rb +0 -222
  114. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  115. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  116. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  117. data/spec/pg/type_map_spec.rb +0 -22
  118. data/spec/pg/type_spec.rb +0 -688
  119. data/spec/pg_spec.rb +0 -50
data/spec/pg/type_spec.rb DELETED
@@ -1,688 +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
- end
516
-
517
- it "should encode Strings as base64 in BinaryDecoder" do
518
- e = PG::BinaryDecoder::ToBase64.new
519
- expect( e.decode("x") ).to eq("eA==")
520
- end
521
-
522
- it "should encode Integers as base64" do
523
- # Not really useful, but ensures that two-pass element and composite element encoders work.
524
- e = PG::TextEncoder::ToBase64.new( elements_type: PG::TextEncoder::Array.new( elements_type: PG::TextEncoder::Integer.new, needs_quotation: false ))
525
- expect( e.encode([1]) ).to eq(["{1}"].pack("m").chomp)
526
- expect( e.encode([12]) ).to eq(["{12}"].pack("m").chomp)
527
- expect( e.encode([123]) ).to eq(["{123}"].pack("m").chomp)
528
- expect( e.encode([1234]) ).to eq(["{1234}"].pack("m").chomp)
529
- expect( e.encode([12345]) ).to eq(["{12345}"].pack("m").chomp)
530
- expect( e.encode([123456]) ).to eq(["{123456}"].pack("m").chomp)
531
- expect( e.encode([1234567]) ).to eq(["{1234567}"].pack("m").chomp)
532
- end
533
-
534
- it "should decode base64 to Strings in TextDecoder" do
535
- e = PG::TextDecoder::FromBase64.new
536
- expect( e.decode("") ).to eq("")
537
- expect( e.decode("eA==") ).to eq("x")
538
- expect( e.decode("eHg=") ).to eq("xx")
539
- expect( e.decode("eHh4") ).to eq("xxx")
540
- expect( e.decode("eHh4eA==") ).to eq("xxxx")
541
- expect( e.decode("eHh4eHg=") ).to eq("xxxxx")
542
- expect( e.decode("AAoJ") ).to eq("\0\n\t")
543
- end
544
-
545
- it "should decode base64 in BinaryEncoder" do
546
- e = PG::BinaryEncoder::FromBase64.new
547
- expect( e.encode("eA==") ).to eq("x")
548
-
549
- e = PG::BinaryEncoder::FromBase64.new( elements_type: PG::TextEncoder::Integer.new )
550
- expect( e.encode(124) ).to eq("124=".unpack("m")[0])
551
- end
552
-
553
- it "should decode base64 to Integers" do
554
- # Not really useful, but ensures that composite element encoders work.
555
- e = PG::TextDecoder::FromBase64.new( elements_type: PG::TextDecoder::Array.new( elements_type: PG::TextDecoder::Integer.new ))
556
- expect( e.decode(["{1}"].pack("m")) ).to eq([1])
557
- expect( e.decode(["{12}"].pack("m")) ).to eq([12])
558
- expect( e.decode(["{123}"].pack("m")) ).to eq([123])
559
- expect( e.decode(["{1234}"].pack("m")) ).to eq([1234])
560
- expect( e.decode(["{12345}"].pack("m")) ).to eq([12345])
561
- expect( e.decode(["{123456}"].pack("m")) ).to eq([123456])
562
- expect( e.decode(["{1234567}"].pack("m")) ).to eq([1234567])
563
- expect( e.decode(["{12345678}"].pack("m")) ).to eq([12345678])
564
-
565
- e = PG::TextDecoder::FromBase64.new( elements_type: PG::BinaryDecoder::Integer.new )
566
- expect( e.decode("ALxhTg==") ).to eq(12345678)
567
- end
568
-
569
- it "should decode base64 with garbage" do
570
- e = PG::TextDecoder::FromBase64.new format: 1
571
- expect( e.decode("=") ).to eq("=".unpack("m")[0])
572
- expect( e.decode("==") ).to eq("==".unpack("m")[0])
573
- expect( e.decode("===") ).to eq("===".unpack("m")[0])
574
- expect( e.decode("====") ).to eq("====".unpack("m")[0])
575
- expect( e.decode("a=") ).to eq("a=".unpack("m")[0])
576
- expect( e.decode("a==") ).to eq("a==".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("aa=") ).to eq("aa=".unpack("m")[0])
580
- expect( e.decode("aa==") ).to eq("aa==".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("aaa=") ).to eq("aaa=".unpack("m")[0])
584
- expect( e.decode("aaa==") ).to eq("aaa==".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("=aa") ).to eq("=aa=".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
- end
592
- end
593
-
594
- describe PG::CopyCoder do
595
- describe PG::TextEncoder::CopyRow do
596
- context "with default typemap" do
597
- let!(:encoder) do
598
- PG::TextEncoder::CopyRow.new
599
- end
600
-
601
- it "should encode different types of Ruby objects" do
602
- expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
603
- to eq("xyz\t123\t2456\t34567\t456789\t5678901\t[1, 2, 3]\t12.1\tabcdefg\t\\N\n")
604
- end
605
- end
606
-
607
- context "with TypeMapByClass" do
608
- let!(:tm) do
609
- tm = PG::TypeMapByClass.new
610
- tm[Integer] = textenc_int
611
- tm[Float] = intenc_incrementer
612
- tm[Array] = PG::TextEncoder::Array.new elements_type: textenc_string
613
- tm
614
- end
615
- let!(:encoder) do
616
- PG::TextEncoder::CopyRow.new type_map: tm
617
- end
618
-
619
- it "should have reasonable default values" do
620
- expect( encoder.name ).to be_nil
621
- expect( encoder.delimiter ).to eq( "\t" )
622
- expect( encoder.null_string ).to eq( "\\N" )
623
- end
624
-
625
- it "copies all attributes with #dup" do
626
- encoder.name = "test"
627
- encoder.delimiter = "#"
628
- encoder.null_string = "NULL"
629
- encoder.type_map = PG::TypeMapByColumn.new []
630
- encoder2 = encoder.dup
631
- expect( encoder.object_id ).to_not eq( encoder2.object_id )
632
- expect( encoder2.name ).to eq( "test" )
633
- expect( encoder2.delimiter ).to eq( "#" )
634
- expect( encoder2.null_string ).to eq( "NULL" )
635
- expect( encoder2.type_map ).to be_a_kind_of( PG::TypeMapByColumn )
636
- end
637
-
638
- describe '#encode' do
639
- it "should encode different types of Ruby objects" do
640
- expect( encoder.encode([]) ).to eq("\n")
641
- expect( encoder.encode(["a"]) ).to eq("a\n")
642
- expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
643
- to eq("xyz\t123\t2456\t34567\t456789\t5678901\t{1,2,3}\t13 \tabcdefg\t\\N\n")
644
- end
645
-
646
- it "should escape special characters" do
647
- expect( encoder.encode([" \0\t\n\r\\"]) ).to eq(" \0#\t#\n#\r#\\\n".gsub("#", "\\"))
648
- end
649
-
650
- it "should escape with different delimiter" do
651
- encoder.delimiter = " "
652
- encoder.null_string = "NULL"
653
- expect( encoder.encode([nil, " ", "\0", "\t", "\n", "\r", "\\"]) ).to eq("NULL # \0 \t #\n #\r #\\\n".gsub("#", "\\"))
654
- end
655
- end
656
- end
657
- end
658
-
659
- describe PG::TextDecoder::CopyRow do
660
- context "with default typemap" do
661
- let!(:decoder) do
662
- PG::TextDecoder::CopyRow.new
663
- end
664
-
665
- describe '#decode' do
666
- it "should decode different types of Ruby objects" do
667
- 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"] )
668
- end
669
- end
670
- end
671
-
672
- context "with TypeMapByColumn" do
673
- let!(:tm) do
674
- PG::TypeMapByColumn.new [textdec_int, textdec_string, intdec_incrementer, nil]
675
- end
676
- let!(:decoder) do
677
- PG::TextDecoder::CopyRow.new type_map: tm
678
- end
679
-
680
- describe '#decode' do
681
- it "should decode different types of Ruby objects" do
682
- 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"] )
683
- end
684
- end
685
- end
686
- end
687
- end
688
- end