pg 0.18.1 → 1.5.6

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