pg 1.0.0 → 1.1.0.pre20180730144600

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 (54) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +0 -6595
  5. data/History.rdoc +52 -0
  6. data/README.rdoc +11 -0
  7. data/Rakefile +1 -1
  8. data/Rakefile.cross +1 -1
  9. data/ext/errorcodes.rb +1 -1
  10. data/ext/extconf.rb +2 -0
  11. data/ext/pg.c +3 -2
  12. data/ext/pg.h +33 -5
  13. data/ext/pg_binary_decoder.c +69 -6
  14. data/ext/pg_binary_encoder.c +1 -1
  15. data/ext/pg_coder.c +52 -3
  16. data/ext/pg_connection.c +290 -103
  17. data/ext/pg_copy_coder.c +10 -5
  18. data/ext/pg_result.c +339 -113
  19. data/ext/pg_text_decoder.c +597 -37
  20. data/ext/pg_text_encoder.c +1 -1
  21. data/ext/pg_tuple.c +540 -0
  22. data/ext/pg_type_map.c +1 -1
  23. data/ext/pg_type_map_all_strings.c +1 -1
  24. data/ext/pg_type_map_by_class.c +1 -1
  25. data/ext/pg_type_map_by_column.c +1 -1
  26. data/ext/pg_type_map_by_mri_type.c +1 -1
  27. data/ext/pg_type_map_by_oid.c +1 -1
  28. data/ext/pg_type_map_in_ruby.c +1 -1
  29. data/ext/util.c +6 -6
  30. data/ext/util.h +2 -2
  31. data/lib/pg.rb +5 -3
  32. data/lib/pg/basic_type_mapping.rb +40 -7
  33. data/lib/pg/coder.rb +1 -1
  34. data/lib/pg/connection.rb +20 -1
  35. data/lib/pg/constants.rb +1 -1
  36. data/lib/pg/exceptions.rb +1 -1
  37. data/lib/pg/result.rb +1 -1
  38. data/lib/pg/text_decoder.rb +19 -23
  39. data/lib/pg/text_encoder.rb +35 -1
  40. data/lib/pg/type_map_by_column.rb +1 -1
  41. data/spec/helpers.rb +39 -7
  42. data/spec/pg/basic_type_mapping_spec.rb +230 -27
  43. data/spec/pg/connection_spec.rb +116 -77
  44. data/spec/pg/result_spec.rb +46 -11
  45. data/spec/pg/type_map_by_class_spec.rb +1 -1
  46. data/spec/pg/type_map_by_column_spec.rb +1 -1
  47. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  48. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  49. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  50. data/spec/pg/type_map_spec.rb +1 -1
  51. data/spec/pg/type_spec.rb +177 -11
  52. data/spec/pg_spec.rb +1 -1
  53. metadata +24 -28
  54. metadata.gz.sig +0 -0
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -39,8 +39,8 @@ describe PG::Result do
39
39
  expect( e.to_a ).to eq [{'a'=>'1', 'b'=>'2'}]
40
40
  end
41
41
 
42
- context "result streaming" do
43
- it "can iterate over all tuples in single row mode" do
42
+ context "result streaming in single row mode" do
43
+ it "can iterate over all rows as Hash" do
44
44
  @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
45
45
  @conn.set_single_row_mode
46
46
  expect(
@@ -56,7 +56,7 @@ describe PG::Result do
56
56
  expect( @conn.get_result ).to be_nil
57
57
  end
58
58
 
59
- it "can iterate over all rows in single row mode" do
59
+ it "can iterate over all rows as Array" do
60
60
  @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
61
61
  @conn.set_single_row_mode
62
62
  expect(
@@ -72,6 +72,22 @@ describe PG::Result do
72
72
  expect( @conn.get_result ).to be_nil
73
73
  end
74
74
 
75
+ it "can iterate over all rows as PG::Tuple" do
76
+ @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
77
+ @conn.set_single_row_mode
78
+ tuples = @conn.get_result.stream_each_tuple.to_a
79
+ expect( tuples[0][0] ).to eq( "2" )
80
+ expect( tuples[1]["a"] ).to eq( "3" )
81
+ expect( tuples.size ).to eq( 3 )
82
+
83
+ tuples = @conn.get_result.enum_for(:stream_each_tuple).to_a
84
+ expect( tuples[-1][-1] ).to eq( "6" )
85
+ expect( tuples[-2]["b"] ).to eq( "1" )
86
+ expect( tuples.size ).to eq( 2 )
87
+
88
+ expect( @conn.get_result ).to be_nil
89
+ end
90
+
75
91
  it "complains when not in single row mode" do
76
92
  @conn.send_query( "SELECT generate_series(2,4)" )
77
93
  expect{
@@ -96,7 +112,7 @@ describe PG::Result do
96
112
  end
97
113
 
98
114
  it "inserts nil AS NULL and return NULL as nil" do
99
- res = @conn.exec("SELECT $1::int AS n", [nil])
115
+ res = @conn.exec_params("SELECT $1::int AS n", [nil])
100
116
  expect( res[0]['n'] ).to be_nil()
101
117
  end
102
118
 
@@ -161,7 +177,7 @@ describe PG::Result do
161
177
  it "returns the same bytes in binary format that are sent in binary format" do
162
178
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
163
179
  bytes = File.open(binary_file, 'rb').read
164
- res = @conn.exec('VALUES ($1::bytea)',
180
+ res = @conn.exec_params('VALUES ($1::bytea)',
165
181
  [ { :value => bytes, :format => 1 } ], 1)
166
182
  expect( res[0]['column1'] ).to eq( bytes )
167
183
  expect( res.getvalue(0,0) ).to eq( bytes )
@@ -173,7 +189,7 @@ describe PG::Result do
173
189
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
174
190
  bytes = File.open(binary_file, 'rb').read
175
191
  @conn.exec("SET standard_conforming_strings=on")
176
- res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
192
+ res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
177
193
  expect( res[0]['column1'] ).to eq( bytes )
178
194
  expect( res.getvalue(0,0) ).to eq( bytes )
179
195
  expect( res.values[0][0] ).to eq( bytes )
@@ -183,7 +199,7 @@ describe PG::Result do
183
199
  it "returns the same bytes in text format that are sent in binary format" do
184
200
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
185
201
  bytes = File.open(binary_file, 'rb').read
186
- res = @conn.exec('VALUES ($1::bytea)',
202
+ res = @conn.exec_params('VALUES ($1::bytea)',
187
203
  [ { :value => bytes, :format => 1 } ])
188
204
  expect( PG::Connection.unescape_bytea(res[0]['column1']) ).to eq( bytes )
189
205
  end
@@ -194,7 +210,7 @@ describe PG::Result do
194
210
 
195
211
  out_bytes = nil
196
212
  @conn.exec("SET standard_conforming_strings=on")
197
- res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
213
+ res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
198
214
  out_bytes = PG::Connection.unescape_bytea(res[0]['column1'])
199
215
  expect( out_bytes ).to eq( in_bytes )
200
216
  end
@@ -205,10 +221,10 @@ describe PG::Result do
205
221
  res = @conn.describe_prepared( 'queryfinder' )
206
222
 
207
223
  expect(
208
- @conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
224
+ @conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
209
225
  ).to eq( 'name' )
210
226
  expect(
211
- @conn.exec( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
227
+ @conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
212
228
  ).to eq( 'text' )
213
229
  end
214
230
 
@@ -343,6 +359,24 @@ describe PG::Result do
343
359
  expect{ res.field_values(:x) }.to raise_error(TypeError)
344
360
  end
345
361
 
362
+ it "can return the values of a single tuple" do
363
+ res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
364
+ expect( res.tuple_values(0) ).to eq( ['1', 'a'] )
365
+ expect( res.tuple_values(1) ).to eq( ['2', 'b'] )
366
+ expect{ res.tuple_values(2) }.to raise_error(IndexError)
367
+ expect{ res.tuple_values(-1) }.to raise_error(IndexError)
368
+ expect{ res.tuple_values("x") }.to raise_error(TypeError)
369
+ end
370
+
371
+ it "can return the values of a single vary lazy tuple" do
372
+ res = @conn.exec( "VALUES(1),(2)" )
373
+ expect( res.tuple(0) ).to be_kind_of( PG::Tuple )
374
+ expect( res.tuple(1) ).to be_kind_of( PG::Tuple )
375
+ expect{ res.tuple(2) }.to raise_error(IndexError)
376
+ expect{ res.tuple(-1) }.to raise_error(IndexError)
377
+ expect{ res.tuple("x") }.to raise_error(TypeError)
378
+ end
379
+
346
380
  it "raises a proper exception for a nonexistant table" do
347
381
  expect {
348
382
  @conn.exec( "SELECT * FROM nonexistant_table" )
@@ -436,6 +470,7 @@ describe PG::Result do
436
470
  expect( res.enum_for(:each).to_a ).to eq( [{'f' => 123}] )
437
471
  expect( res.column_values(0) ).to eq( [123] )
438
472
  expect( res.field_values('f') ).to eq( [123] )
473
+ expect( res.tuple_values(0) ).to eq( [123] )
439
474
  end
440
475
 
441
476
  it "should be usable for several querys" do
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative '../helpers'
@@ -1,7 +1,8 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require 'pg'
5
+ require 'time'
5
6
 
6
7
 
7
8
  describe "PG::Type derivations" do
@@ -15,6 +16,9 @@ describe "PG::Type derivations" do
15
16
  let!(:textdec_string) { PG::TextDecoder::String.new }
16
17
  let!(:textenc_timestamp) { PG::TextEncoder::TimestampWithoutTimeZone.new }
17
18
  let!(:textdec_timestamp) { PG::TextDecoder::TimestampWithoutTimeZone.new }
19
+ let!(:textenc_timestamputc) { PG::TextEncoder::TimestampUtc.new }
20
+ let!(:textdec_timestamputc) { PG::TextDecoder::TimestampUtc.new }
21
+ let!(:textdec_timestampul) { PG::TextDecoder::TimestampUtcToLocal.new }
18
22
  let!(:textenc_timestamptz) { PG::TextEncoder::TimestampWithTimeZone.new }
19
23
  let!(:textdec_timestamptz) { PG::TextDecoder::TimestampWithTimeZone.new }
20
24
  let!(:textenc_bytea) { PG::TextEncoder::Bytea.new }
@@ -96,21 +100,35 @@ describe "PG::Type derivations" do
96
100
  end
97
101
 
98
102
  context 'timestamps' do
99
- it 'decodes timestamps without timezone' do
100
- expect( textdec_timestamp.decode('2016-01-02 23:23:59.123456') ).
101
- to be_within(0.000001).of( Time.new(2016,01,02, 23, 23, 59.123456) )
103
+ it 'decodes timestamps without timezone as local time' do
104
+ expect( textdec_timestamp.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
105
+ to eq( Time.new(2016,1,2, 23,23,59.123456).iso8601(5) )
106
+ expect( textdec_timestamp.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
107
+ to eq( Time.new(2016,8,2, 23,23,59.123456).iso8601(5) )
108
+ end
109
+ it 'decodes timestamps with UTC time and returns UTC timezone' do
110
+ expect( textdec_timestamputc.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
111
+ to eq( Time.utc(2016,1,2, 23,23,59.123456).iso8601(5) )
112
+ expect( textdec_timestamputc.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
113
+ to eq( Time.utc(2016,8,2, 23,23,59.123456).iso8601(5) )
114
+ end
115
+ it 'decodes timestamps with UTC time and returns local timezone' do
116
+ expect( textdec_timestampul.decode('2016-01-02 23:23:59.123456').iso8601(5) ).
117
+ to eq( Time.utc(2016,1,2, 23,23,59.123456).getlocal.iso8601(5) )
118
+ expect( textdec_timestampul.decode('2016-08-02 23:23:59.123456').iso8601(5) ).
119
+ to eq( Time.utc(2016,8,2, 23,23,59.123456).getlocal.iso8601(5) )
102
120
  end
103
121
  it 'decodes timestamps with hour timezone' do
104
- expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-04') ).
105
- to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:00") )
106
- expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511+10') ).
107
- to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "+10:00") )
122
+ expect( textdec_timestamptz.decode('2016-01-02 23:23:59.123456-04').iso8601(5) ).
123
+ to eq( Time.new(2016,1,2, 23,23,59.123456, "-04:00").iso8601(5) )
124
+ expect( textdec_timestamptz.decode('2016-08-02 23:23:59.123456+10').iso8601(5) ).
125
+ to eq( Time.new(2016,8,2, 23,23,59.123456, "+10:00").iso8601(5) )
108
126
  end
109
127
  it 'decodes timestamps with hour:minute timezone' do
110
128
  expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-04:15') ).
111
129
  to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:15") )
112
- expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511-0430') ).
113
- to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "-04:30") )
130
+ expect( textdec_timestamptz.decode('2015-07-26 17:26:42.691511-04:30') ).
131
+ to be_within(0.000001).of( Time.new(2015,07,26, 17, 26, 42.691511, "-04:30") )
114
132
  expect( textdec_timestamptz.decode('2015-01-26 17:26:42.691511+10:45') ).
115
133
  to be_within(0.000001).of( Time.new(2015,01,26, 17, 26, 42.691511, "+10:45") )
116
134
  end
@@ -121,6 +139,81 @@ describe "PG::Type derivations" do
121
139
  expect( textdec_timestamptz.decode('1916-01-01 00:00:00-00:25:21') ).
122
140
  to be_within(0.000001).of( Time.new(1916, 1, 1, 0, 0, 0, "-00:25:21") )
123
141
  end
142
+ it 'decodes timestamps with date before 1823' do
143
+ expect( textdec_timestamp.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
144
+ to eq( Time.new(1822,01,02, 23, 23, 59.123456).iso8601(5) )
145
+ expect( textdec_timestamputc.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
146
+ to eq( Time.utc(1822,01,02, 23, 23, 59.123456).iso8601(5) )
147
+ expect( textdec_timestampul.decode('1822-01-02 23:23:59.123456').iso8601(5) ).
148
+ to eq( Time.utc(1822,01,02, 23, 23, 59.123456).getlocal.iso8601(5) )
149
+ expect( textdec_timestamptz.decode('1822-01-02 23:23:59.123456+04').iso8601(5) ).
150
+ to eq( Time.new(1822,01,02, 23, 23, 59.123456, "+04:00").iso8601(5) )
151
+ end
152
+ it 'decodes timestamps with date after 2116' do
153
+ expect( textdec_timestamp.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
154
+ to eq( Time.new(2117,01,02, 23, 23, 59.123456).iso8601(5) )
155
+ expect( textdec_timestamputc.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
156
+ to eq( Time.utc(2117,01,02, 23, 23, 59.123456).iso8601(5) )
157
+ expect( textdec_timestampul.decode('2117-01-02 23:23:59.123456').iso8601(5) ).
158
+ to eq( Time.utc(2117,01,02, 23, 23, 59.123456).getlocal.iso8601(5) )
159
+ expect( textdec_timestamptz.decode('2117-01-02 23:23:59.123456+04').iso8601(5) ).
160
+ to eq( Time.new(2117,01,02, 23, 23, 59.123456, "+04:00").iso8601(5) )
161
+ end
162
+ it 'decodes timestamps with variable number of digits for the useconds part' do
163
+ sec = "59.12345678912345"
164
+ (4..sec.length).each do |i|
165
+ expect( textdec_timestamp.decode("2016-01-02 23:23:#{sec[0,i]}") ).
166
+ to be_within(0.000001).of( Time.new(2016,01,02, 23, 23, sec[0,i].to_f) )
167
+ end
168
+ end
169
+ it 'decodes timestamps with leap-second' do
170
+ expect( textdec_timestamp.decode('1998-12-31 23:59:60.1234') ).
171
+ to be_within(0.000001).of( Time.new(1998,12,31, 23, 59, 60.1234) )
172
+ end
173
+
174
+ def textdec_timestamptz_decode_should_fail(str)
175
+ expect(textdec_timestamptz.decode(str)).to eq(str)
176
+ end
177
+
178
+ it 'fails when the timestamp is an empty string' do
179
+ textdec_timestamptz_decode_should_fail('')
180
+ end
181
+ it 'fails when the timestamp contains values with less digits than expected' do
182
+ textdec_timestamptz_decode_should_fail('2016-0-02 23:23:59.123456+00:25:21')
183
+ textdec_timestamptz_decode_should_fail('2016-01-0 23:23:59.123456+00:25:21')
184
+ textdec_timestamptz_decode_should_fail('2016-01-02 2:23:59.123456+00:25:21')
185
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:2:59.123456+00:25:21')
186
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:5.123456+00:25:21')
187
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.+00:25:21')
188
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+0:25:21')
189
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:2:21')
190
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:25:2')
191
+ end
192
+ it 'fails when the timestamp contains values with more digits than expected' do
193
+ textdec_timestamptz_decode_should_fail('2016-011-02 23:23:59.123456+00:25:21')
194
+ textdec_timestamptz_decode_should_fail('2016-01-022 23:23:59.123456+00:25:21')
195
+ textdec_timestamptz_decode_should_fail('2016-01-02 233:23:59.123456+00:25:21')
196
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:233:59.123456+00:25:21')
197
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:599.123456+00:25:21')
198
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+000:25:21')
199
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:255:21')
200
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456+00:25:211')
201
+ end
202
+ it 'fails when the timestamp contains values with invalid characters' do
203
+ str = '2013-01-02 23:23:59.123456+00:25:21'
204
+ str.length.times do |i|
205
+ textdec_timestamptz_decode_should_fail(str[0,i] + "x" + str[i+1..-1])
206
+ end
207
+ end
208
+ it 'fails when the timestamp contains leading characters' do
209
+ textdec_timestamptz_decode_should_fail(' 2016-01-02 23:23:59.123456')
210
+ end
211
+ it 'fails when the timestamp contains trailing characters' do
212
+ textdec_timestamptz_decode_should_fail('2016-01-02 23:23:59.123456 ')
213
+ end
214
+ it 'fails when the timestamp contains non ASCII character' do
215
+ textdec_timestamptz_decode_should_fail('2016-01ª02 23:23:59.123456')
216
+ end
124
217
  end
125
218
 
126
219
  context 'identifier quotation' do
@@ -215,6 +308,27 @@ describe "PG::Type derivations" do
215
308
  expect( textenc_bytea.encode("\x00\x01\x02\x03\xef".b) ).to eq( "\\x00010203ef" )
216
309
  end
217
310
 
311
+ context 'timestamps' do
312
+ it 'encodes timestamps without timezone' do
313
+ expect( textenc_timestamp.encode(Time.new(2016,1,2, 23, 23, 59.123456, 3*60*60)) ).
314
+ to match( /^2016-01-02 23:23:59.12345\d+$/ )
315
+ expect( textenc_timestamp.encode(Time.new(2016,8,2, 23, 23, 59.123456, 3*60*60)) ).
316
+ to match( /^2016-08-02 23:23:59.12345\d+$/ )
317
+ end
318
+ it 'encodes timestamps with UTC timezone' do
319
+ expect( textenc_timestamputc.encode(Time.new(2016,1,2, 23, 23, 59.123456, 3*60*60)) ).
320
+ to match( /^2016-01-02 20:23:59.12345\d+$/ )
321
+ expect( textenc_timestamputc.encode(Time.new(2016,8,2, 23, 23, 59.123456, 3*60*60)) ).
322
+ to match( /^2016-08-02 20:23:59.12345\d+$/ )
323
+ end
324
+ it 'encodes timestamps with hour timezone' do
325
+ expect( textenc_timestamptz.encode(Time.new(2016,1,02, 23, 23, 59.123456, -4*60*60)) ).
326
+ to match( /^2016-01-02 23:23:59.12345\d+ \-04:00$/ )
327
+ expect( textenc_timestamptz.encode(Time.new(2016,8,02, 23, 23, 59.123456, 10*60*60)) ).
328
+ to match( /^2016-08-02 23:23:59.12345\d+ \+10:00$/ )
329
+ end
330
+ end
331
+
218
332
  context 'identifier quotation' do
219
333
  it 'should quote and escape identifier' do
220
334
  quoted_type = PG::TextEncoder::Identifier.new
@@ -303,6 +417,7 @@ describe "PG::Type derivations" do
303
417
  describe "Array types" do
304
418
  let!(:textenc_string_array) { PG::TextEncoder::Array.new elements_type: textenc_string }
305
419
  let!(:textdec_string_array) { PG::TextDecoder::Array.new elements_type: textdec_string }
420
+ let!(:textdec_string_array_raise) { PG::TextDecoder::Array.new elements_type: textdec_string, flags: PG::Coder:: FORMAT_ERROR_TO_RAISE }
306
421
  let!(:textenc_int_array) { PG::TextEncoder::Array.new elements_type: textenc_int, needs_quotation: false }
307
422
  let!(:textdec_int_array) { PG::TextDecoder::Array.new elements_type: textdec_int, needs_quotation: false }
308
423
  let!(:textenc_float_array) { PG::TextEncoder::Array.new elements_type: textenc_float, needs_quotation: false }
@@ -368,6 +483,57 @@ describe "PG::Type derivations" do
368
483
  it 'respects a different delimiter' do
369
484
  expect( textdec_string_array_with_delimiter.decode(%[{1;2;3}]) ).to eq( ['1','2','3'] )
370
485
  end
486
+
487
+ it 'ignores array dimensions' do
488
+ expect( textdec_string_array.decode(%[[2:4]={1,2,3}]) ).to eq( ['1','2','3'] )
489
+ expect( textdec_string_array.decode(%[[]={1,2,3}]) ).to eq( ['1','2','3'] )
490
+ expect( textdec_string_array.decode(%[ [-1:+2]= {4,3,2,1}]) ).to eq( ['4','3','2','1'] )
491
+ end
492
+
493
+ it 'ignores spaces after array' do
494
+ expect( textdec_string_array.decode(%[[2:4]={1,2,3} ]) ).to eq( ['1','2','3'] )
495
+ expect( textdec_string_array.decode(%[{1,2,3} ]) ).to eq( ['1','2','3'] )
496
+ end
497
+
498
+ describe "with malformed syntax are deprecated" do
499
+ it 'accepts broken array dimensions' do
500
+ expect( textdec_string_array.decode(%([2:4={1,2,3})) ).to eq([['1','2','3']])
501
+ expect( textdec_string_array.decode(%(2:4]={1,2,3})) ).to eq([['1','2','3']])
502
+ expect( textdec_string_array.decode(%(={1,2,3})) ).to eq([['1','2','3']])
503
+ expect( textdec_string_array.decode(%([x]={1,2,3})) ).to eq([['1','2','3']])
504
+ expect( textdec_string_array.decode(%([]{1,2,3})) ).to eq([['1','2','3']])
505
+ expect( textdec_string_array.decode(%(1,2,3)) ).to eq(['','2'])
506
+ end
507
+
508
+ it 'accepts malformed arrays' do
509
+ expect( textdec_string_array.decode(%({1,2,3)) ).to eq(['1','2'])
510
+ expect( textdec_string_array.decode(%({1,2,3}})) ).to eq(['1','2','3'])
511
+ expect( textdec_string_array.decode(%({1,2,3}x)) ).to eq(['1','2','3'])
512
+ expect( textdec_string_array.decode(%({{1,2},{2,3})) ).to eq([['1','2'],['2','3']])
513
+ expect( textdec_string_array.decode(%({{1,2},{2,3}}x)) ).to eq([['1','2'],['2','3']])
514
+ expect( textdec_string_array.decode(%({[1,2},{2,3}}})) ).to eq(['[1','2'])
515
+ end
516
+ end
517
+
518
+ describe "with malformed syntax are raised with pg-2.0+" do
519
+ it 'complains about broken array dimensions' do
520
+ expect{ textdec_string_array_raise.decode(%([2:4={1,2,3})) }.to raise_error(TypeError)
521
+ expect{ textdec_string_array_raise.decode(%(2:4]={1,2,3})) }.to raise_error(TypeError)
522
+ expect{ textdec_string_array_raise.decode(%(={1,2,3})) }.to raise_error(TypeError)
523
+ expect{ textdec_string_array_raise.decode(%([x]={1,2,3})) }.to raise_error(TypeError)
524
+ expect{ textdec_string_array_raise.decode(%([]{1,2,3})) }.to raise_error(TypeError)
525
+ expect{ textdec_string_array_raise.decode(%(1,2,3)) }.to raise_error(TypeError)
526
+ end
527
+
528
+ it 'complains about malformed array' do
529
+ expect{ textdec_string_array_raise.decode(%({1,2,3)) }.to raise_error(TypeError)
530
+ expect{ textdec_string_array_raise.decode(%({1,2,3}})) }.to raise_error(TypeError)
531
+ expect{ textdec_string_array_raise.decode(%({1,2,3}x)) }.to raise_error(TypeError)
532
+ expect{ textdec_string_array_raise.decode(%({{1,2},{2,3})) }.to raise_error(TypeError)
533
+ expect{ textdec_string_array_raise.decode(%({{1,2},{2,3}}x)) }.to raise_error(TypeError)
534
+ expect{ textdec_string_array_raise.decode(%({[1,2},{2,3}}})) }.to raise_error(TypeError)
535
+ end
536
+ end
371
537
  end
372
538
 
373
539
  context 'bytea' do
@@ -746,7 +912,7 @@ describe "PG::Type derivations" do
746
912
  end
747
913
 
748
914
  describe '#decode' do
749
- it "should decode different types of Ruby objects" do
915
+ it "should decode COPY text format to array of strings" do
750
916
  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"] )
751
917
  end
752
918
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
1
+ # -*- rspec -*-
2
2
  # encoding: utf-8
3
3
 
4
4
  require_relative 'helpers'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0.pre20180730144600
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Granger
@@ -11,32 +11,26 @@ bindir: bin
11
11
  cert_chain:
12
12
  - |
13
13
  -----BEGIN CERTIFICATE-----
14
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- +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
- 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
- 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
- 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
- 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
- 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
- vG64FEnLnD1bm39T5UaFIRmo+abZtfpg2QhwKvPbPjOicau2+m+MDQ2Cc3tgyaC3
31
- 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
- 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
- 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
- 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
- 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
- X0qdrKi+2aZZ0NGuFj9AItBsVmAvkBGIpX4TEKQp5haEbPpmaqO5nIIhV26PXmyT
37
- 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
31
+ Lvw=
38
32
  -----END CERTIFICATE-----
39
- date: 2018-01-10 00:00:00.000000000 Z
33
+ date: 2018-07-30 00:00:00.000000000 Z
40
34
  dependencies:
41
35
  - !ruby/object:Gem::Dependency
42
36
  name: hoe-mercurial
@@ -218,6 +212,7 @@ extra_rdoc_files:
218
212
  - ext/pg_result.c
219
213
  - ext/pg_text_decoder.c
220
214
  - ext/pg_text_encoder.c
215
+ - ext/pg_tuple.c
221
216
  - ext/pg_type_map.c
222
217
  - ext/pg_type_map_all_strings.c
223
218
  - ext/pg_type_map_by_class.c
@@ -258,6 +253,7 @@ files:
258
253
  - ext/pg_result.c
259
254
  - ext/pg_text_decoder.c
260
255
  - ext/pg_text_encoder.c
256
+ - ext/pg_tuple.c
261
257
  - ext/pg_type_map.c
262
258
  - ext/pg_type_map_all_strings.c
263
259
  - ext/pg_type_map_by_class.c
@@ -311,9 +307,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
311
307
  version: 2.0.0
312
308
  required_rubygems_version: !ruby/object:Gem::Requirement
313
309
  requirements:
314
- - - ">="
310
+ - - ">"
315
311
  - !ruby/object:Gem::Version
316
- version: '0'
312
+ version: 1.3.1
317
313
  requirements: []
318
314
  rubyforge_project:
319
315
  rubygems_version: 2.7.3