pg 0.18.1 → 1.5.6

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