pg 0.17.1-x86-mingw32 → 0.18.0.pre20141017160319-x86-mingw32
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +1885 -169
- data/History.rdoc +6 -0
- data/Manifest.txt +25 -1
- data/README.rdoc +47 -0
- data/Rakefile +21 -12
- data/Rakefile.cross +39 -33
- data/ext/extconf.rb +27 -26
- data/ext/pg.c +73 -19
- data/ext/pg.h +194 -6
- data/ext/pg_binary_decoder.c +160 -0
- data/ext/pg_binary_encoder.c +160 -0
- data/ext/pg_coder.c +473 -0
- data/ext/pg_connection.c +872 -534
- data/ext/pg_copy_coder.c +557 -0
- data/ext/pg_result.c +266 -111
- data/ext/pg_text_decoder.c +424 -0
- data/ext/pg_text_encoder.c +631 -0
- data/ext/pg_type_map.c +113 -0
- data/ext/pg_type_map_all_strings.c +113 -0
- data/ext/pg_type_map_by_column.c +254 -0
- data/ext/pg_type_map_by_mri_type.c +266 -0
- data/ext/pg_type_map_by_oid.c +341 -0
- data/ext/util.c +149 -0
- data/ext/util.h +65 -0
- data/lib/1.9/pg_ext.so +0 -0
- data/lib/2.0/pg_ext.so +0 -0
- data/lib/2.1/pg_ext.so +0 -0
- data/lib/i386-mingw32/libpq.dll +0 -0
- data/lib/pg.rb +11 -1
- data/lib/pg/basic_type_mapping.rb +377 -0
- data/lib/pg/coder.rb +74 -0
- data/lib/pg/connection.rb +43 -1
- data/lib/pg/result.rb +13 -3
- data/lib/pg/text_decoder.rb +42 -0
- data/lib/pg/text_encoder.rb +27 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/spec/{lib/helpers.rb → helpers.rb} +95 -35
- data/spec/pg/basic_type_mapping_spec.rb +251 -0
- data/spec/pg/connection_spec.rb +416 -214
- data/spec/pg/result_spec.rb +146 -116
- data/spec/pg/type_map_by_column_spec.rb +135 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
- data/spec/pg/type_map_by_oid_spec.rb +133 -0
- data/spec/pg/type_map_spec.rb +39 -0
- data/spec/pg/type_spec.rb +649 -0
- data/spec/pg_spec.rb +10 -18
- metadata +130 -52
- metadata.gz.sig +0 -0
- data/lib/1.8/pg_ext.so +0 -0
data/spec/pg/connection_spec.rb
CHANGED
@@ -1,49 +1,14 @@
|
|
1
1
|
#!/usr/bin/env rspec
|
2
2
|
#encoding: utf-8
|
3
3
|
|
4
|
-
|
5
|
-
require 'pathname'
|
4
|
+
require_relative '../helpers'
|
6
5
|
|
7
|
-
basedir = Pathname( __FILE__ ).dirname.parent.parent
|
8
|
-
libdir = basedir + 'lib'
|
9
|
-
|
10
|
-
$LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
|
11
|
-
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
12
|
-
}
|
13
|
-
|
14
|
-
require 'rspec'
|
15
|
-
require 'spec/lib/helpers'
|
16
6
|
require 'timeout'
|
17
7
|
require 'socket'
|
18
8
|
require 'pg'
|
19
9
|
|
20
10
|
describe PG::Connection do
|
21
11
|
|
22
|
-
before( :all ) do
|
23
|
-
@conn = setup_testing_db( described_class.name )
|
24
|
-
end
|
25
|
-
|
26
|
-
before( :each ) do
|
27
|
-
@conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
|
28
|
-
if PG.respond_to?( :library_version )
|
29
|
-
@conn.exec_params %Q{SET application_name TO '%s'} %
|
30
|
-
[@conn.escape_string(example.description[0,60])]
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
after( :each ) do
|
35
|
-
@conn.exec( 'ROLLBACK' ) unless example.metadata[:without_transaction]
|
36
|
-
end
|
37
|
-
|
38
|
-
after( :all ) do
|
39
|
-
teardown_testing_db( @conn )
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
#
|
44
|
-
# Examples
|
45
|
-
#
|
46
|
-
|
47
12
|
it "can create a connection option string from a Hash of options" do
|
48
13
|
optstring = described_class.parse_connect_args(
|
49
14
|
:host => 'pgsql.example.com',
|
@@ -51,54 +16,55 @@ describe PG::Connection do
|
|
51
16
|
'sslmode' => 'require'
|
52
17
|
)
|
53
18
|
|
54
|
-
optstring.
|
55
|
-
optstring.
|
56
|
-
optstring.
|
57
|
-
optstring.
|
19
|
+
expect( optstring ).to be_a( String )
|
20
|
+
expect( optstring ).to match( /(^|\s)host='pgsql.example.com'/ )
|
21
|
+
expect( optstring ).to match( /(^|\s)dbname='db01'/ )
|
22
|
+
expect( optstring ).to match( /(^|\s)sslmode='require'/ )
|
58
23
|
end
|
59
24
|
|
60
25
|
it "can create a connection option string from positional parameters" do
|
61
26
|
optstring = described_class.parse_connect_args( 'pgsql.example.com', nil, '-c geqo=off', nil,
|
62
27
|
'sales' )
|
63
28
|
|
64
|
-
optstring.
|
65
|
-
optstring.
|
66
|
-
optstring.
|
67
|
-
optstring.
|
29
|
+
expect( optstring ).to be_a( String )
|
30
|
+
expect( optstring ).to match( /(^|\s)host='pgsql.example.com'/ )
|
31
|
+
expect( optstring ).to match( /(^|\s)dbname='sales'/ )
|
32
|
+
expect( optstring ).to match( /(^|\s)options='-c geqo=off'/ )
|
68
33
|
|
69
|
-
optstring.
|
70
|
-
optstring.
|
34
|
+
expect( optstring ).to_not match( /port=/ )
|
35
|
+
expect( optstring ).to_not match( /tty=/ )
|
71
36
|
end
|
72
37
|
|
73
38
|
it "can create a connection option string from a mix of positional and hash parameters" do
|
74
39
|
optstring = described_class.parse_connect_args( 'pgsql.example.com',
|
75
40
|
:dbname => 'licensing', :user => 'jrandom' )
|
76
41
|
|
77
|
-
optstring.
|
78
|
-
optstring.
|
79
|
-
optstring.
|
80
|
-
optstring.
|
42
|
+
expect( optstring ).to be_a( String )
|
43
|
+
expect( optstring ).to match( /(^|\s)host='pgsql.example.com'/ )
|
44
|
+
expect( optstring ).to match( /(^|\s)dbname='licensing'/ )
|
45
|
+
expect( optstring ).to match( /(^|\s)user='jrandom'/ )
|
81
46
|
end
|
82
47
|
|
83
48
|
it "escapes single quotes and backslashes in connection parameters" do
|
84
|
-
|
85
|
-
|
49
|
+
expect(
|
50
|
+
described_class.parse_connect_args( "DB 'browser' \\" )
|
51
|
+
).to match( /host='DB \\'browser\\' \\\\'/ )
|
86
52
|
|
87
53
|
end
|
88
54
|
|
89
55
|
it "connects with defaults if no connection parameters are given" do
|
90
|
-
described_class.parse_connect_args.
|
56
|
+
expect( described_class.parse_connect_args ).to eq( '' )
|
91
57
|
end
|
92
58
|
|
93
59
|
it "connects successfully with connection string" do
|
94
60
|
tmpconn = described_class.connect(@conninfo)
|
95
|
-
tmpconn.status.
|
61
|
+
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
96
62
|
tmpconn.finish
|
97
63
|
end
|
98
64
|
|
99
65
|
it "connects using 7 arguments converted to strings" do
|
100
66
|
tmpconn = described_class.connect('localhost', @port, nil, nil, :test, nil, nil)
|
101
|
-
tmpconn.status.
|
67
|
+
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
102
68
|
tmpconn.finish
|
103
69
|
end
|
104
70
|
|
@@ -107,7 +73,7 @@ describe PG::Connection do
|
|
107
73
|
:host => 'localhost',
|
108
74
|
:port => @port,
|
109
75
|
:dbname => :test)
|
110
|
-
tmpconn.status.
|
76
|
+
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
111
77
|
tmpconn.finish
|
112
78
|
end
|
113
79
|
|
@@ -117,7 +83,7 @@ describe PG::Connection do
|
|
117
83
|
:port => @port,
|
118
84
|
:dbname => :test,
|
119
85
|
:keepalives => 1)
|
120
|
-
tmpconn.status.
|
86
|
+
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
121
87
|
tmpconn.finish
|
122
88
|
end
|
123
89
|
|
@@ -129,7 +95,7 @@ describe PG::Connection do
|
|
129
95
|
|
130
96
|
it "can connect asynchronously", :socket_io do
|
131
97
|
tmpconn = described_class.connect_start( @conninfo )
|
132
|
-
tmpconn.
|
98
|
+
expect( tmpconn ).to be_a( described_class )
|
133
99
|
socket = tmpconn.socket_io
|
134
100
|
status = tmpconn.connect_poll
|
135
101
|
|
@@ -145,7 +111,7 @@ describe PG::Connection do
|
|
145
111
|
status = tmpconn.connect_poll
|
146
112
|
end
|
147
113
|
|
148
|
-
tmpconn.status.
|
114
|
+
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
149
115
|
tmpconn.finish
|
150
116
|
end
|
151
117
|
|
@@ -153,7 +119,7 @@ describe PG::Connection do
|
|
153
119
|
conn = nil
|
154
120
|
|
155
121
|
described_class.connect_start(@conninfo) do |tmpconn|
|
156
|
-
tmpconn.
|
122
|
+
expect( tmpconn ).to be_a( described_class )
|
157
123
|
conn = tmpconn
|
158
124
|
socket = tmpconn.socket_io
|
159
125
|
status = tmpconn.connect_poll
|
@@ -171,10 +137,10 @@ describe PG::Connection do
|
|
171
137
|
status = tmpconn.connect_poll
|
172
138
|
end
|
173
139
|
|
174
|
-
tmpconn.status.
|
140
|
+
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
175
141
|
end
|
176
142
|
|
177
|
-
conn.
|
143
|
+
expect( conn ).to be_finished()
|
178
144
|
end
|
179
145
|
|
180
146
|
it "raises proper error when sending fails" do
|
@@ -188,7 +154,7 @@ describe PG::Connection do
|
|
188
154
|
res = @conn.exec(%[SELECT COUNT(*) AS n FROM pg_stat_activity
|
189
155
|
WHERE usename IS NOT NULL])
|
190
156
|
# there's still the global @conn, but should be no more
|
191
|
-
res[0]['n'].
|
157
|
+
expect( res[0]['n'] ).to eq( '1' )
|
192
158
|
end
|
193
159
|
|
194
160
|
|
@@ -248,7 +214,7 @@ describe PG::Connection do
|
|
248
214
|
expected_trace_output.sub!( /From backend> "SELECT 1"/, 'From backend> "SELECT"' )
|
249
215
|
end
|
250
216
|
|
251
|
-
trace_data.
|
217
|
+
expect( trace_data ).to eq( expected_trace_output )
|
252
218
|
end
|
253
219
|
|
254
220
|
it "allows a query to be cancelled" do
|
@@ -259,10 +225,12 @@ describe PG::Connection do
|
|
259
225
|
if(tmpres.result_status != PG::PGRES_TUPLES_OK)
|
260
226
|
error = true
|
261
227
|
end
|
262
|
-
error.
|
228
|
+
expect( error ).to eq( true )
|
263
229
|
end
|
264
230
|
|
265
231
|
it "can stop a thread that runs a blocking query with async_exec" do
|
232
|
+
pending "this does not work on Rubinius" if RUBY_ENGINE=='rbx'
|
233
|
+
|
266
234
|
start = Time.now
|
267
235
|
t = Thread.new do
|
268
236
|
@conn.async_exec( 'select pg_sleep(10)' )
|
@@ -271,10 +239,10 @@ describe PG::Connection do
|
|
271
239
|
|
272
240
|
t.kill
|
273
241
|
t.join
|
274
|
-
(Time.now - start).
|
242
|
+
expect( (Time.now - start) ).to be < 10
|
275
243
|
end
|
276
244
|
|
277
|
-
it "should work together with signal handlers" do
|
245
|
+
it "should work together with signal handlers", :unix do
|
278
246
|
signal_received = false
|
279
247
|
trap 'USR1' do
|
280
248
|
signal_received = true
|
@@ -285,7 +253,7 @@ describe PG::Connection do
|
|
285
253
|
Process.kill("USR1", Process.pid)
|
286
254
|
end
|
287
255
|
@conn.exec("select pg_sleep(0.3)")
|
288
|
-
signal_received.
|
256
|
+
expect( signal_received ).to be_truthy
|
289
257
|
|
290
258
|
signal_received = false
|
291
259
|
Thread.new do
|
@@ -293,7 +261,7 @@ describe PG::Connection do
|
|
293
261
|
Process.kill("USR1", Process.pid)
|
294
262
|
end
|
295
263
|
@conn.async_exec("select pg_sleep(0.3)")
|
296
|
-
signal_received.
|
264
|
+
expect( signal_received ).to be_truthy
|
297
265
|
end
|
298
266
|
|
299
267
|
|
@@ -305,15 +273,19 @@ describe PG::Connection do
|
|
305
273
|
res = nil
|
306
274
|
@conn.exec( "CREATE TABLE pie ( flavor TEXT )" )
|
307
275
|
|
308
|
-
|
309
|
-
|
310
|
-
@conn.
|
311
|
-
|
312
|
-
|
313
|
-
|
276
|
+
begin
|
277
|
+
expect {
|
278
|
+
res = @conn.transaction do
|
279
|
+
@conn.exec( "INSERT INTO pie VALUES ('rhubarb'), ('cherry'), ('schizophrenia')" )
|
280
|
+
raise "Oh noes! All pie is gone!"
|
281
|
+
end
|
282
|
+
}.to raise_exception( RuntimeError, /all pie is gone/i )
|
314
283
|
|
315
|
-
|
316
|
-
|
284
|
+
res = @conn.exec( "SELECT * FROM pie" )
|
285
|
+
expect( res.ntuples ).to eq( 0 )
|
286
|
+
ensure
|
287
|
+
@conn.exec( "DROP TABLE pie" )
|
288
|
+
end
|
317
289
|
end
|
318
290
|
|
319
291
|
it "returns the block result from Connection#transaction" do
|
@@ -323,7 +295,7 @@ describe PG::Connection do
|
|
323
295
|
res = @conn.transaction do
|
324
296
|
"transaction result"
|
325
297
|
end
|
326
|
-
res.
|
298
|
+
expect( res ).to eq( "transaction result" )
|
327
299
|
end
|
328
300
|
|
329
301
|
it "not read past the end of a large object" do
|
@@ -331,9 +303,9 @@ describe PG::Connection do
|
|
331
303
|
oid = @conn.lo_create( 0 )
|
332
304
|
fd = @conn.lo_open( oid, PG::INV_READ|PG::INV_WRITE )
|
333
305
|
@conn.lo_write( fd, "foobar" )
|
334
|
-
@conn.lo_read( fd, 10 ).
|
306
|
+
expect( @conn.lo_read( fd, 10 ) ).to be_nil()
|
335
307
|
@conn.lo_lseek( fd, 0, PG::SEEK_SET )
|
336
|
-
@conn.lo_read( fd, 10 ).
|
308
|
+
expect( @conn.lo_read( fd, 10 ) ).to eq( 'foobar' )
|
337
309
|
end
|
338
310
|
end
|
339
311
|
|
@@ -345,7 +317,7 @@ describe PG::Connection do
|
|
345
317
|
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
346
318
|
|
347
319
|
res = @conn.exec( "SELECT name FROM students WHERE age >= $1", [6] )
|
348
|
-
res.values.
|
320
|
+
expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
|
349
321
|
end
|
350
322
|
|
351
323
|
it "supports explicitly calling #exec_params" do
|
@@ -355,9 +327,32 @@ describe PG::Connection do
|
|
355
327
|
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
356
328
|
|
357
329
|
res = @conn.exec_params( "SELECT name FROM students WHERE age >= $1", [6] )
|
358
|
-
res.values.
|
330
|
+
expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
|
359
331
|
end
|
360
332
|
|
333
|
+
it "supports hash form parameters for #exec_params" do
|
334
|
+
hash_param_bin = { value: ["00ff"].pack("H*"), type: 17, format: 1 }
|
335
|
+
hash_param_nil = { value: nil, type: 17, format: 1 }
|
336
|
+
res = @conn.exec_params( "SELECT $1, $2",
|
337
|
+
[ hash_param_bin, hash_param_nil ] )
|
338
|
+
expect( res.values ).to eq( [["\\x00ff", nil]] )
|
339
|
+
expect( result_typenames(res) ).to eq( ['bytea', 'bytea'] )
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should work with arbitrary number of params" do
|
343
|
+
begin
|
344
|
+
3.step( 12, 0.2 ) do |exp|
|
345
|
+
num_params = (2 ** exp).to_i
|
346
|
+
sql = num_params.times.map{|n| "$#{n+1}::INT" }.join(",")
|
347
|
+
params = num_params.times.to_a
|
348
|
+
res = @conn.exec_params( "SELECT #{sql}", params )
|
349
|
+
expect( res.nfields ).to eq( num_params )
|
350
|
+
expect( res.values ).to eq( [num_params.times.map(&:to_s)] )
|
351
|
+
end
|
352
|
+
rescue PG::ProgramLimitExceeded
|
353
|
+
# Stop silently if the server complains about too many params
|
354
|
+
end
|
355
|
+
end
|
361
356
|
|
362
357
|
it "can wait for NOTIFY events" do
|
363
358
|
@conn.exec( 'ROLLBACK' )
|
@@ -373,7 +368,7 @@ describe PG::Connection do
|
|
373
368
|
end
|
374
369
|
end
|
375
370
|
|
376
|
-
@conn.wait_for_notify( 10 ).
|
371
|
+
expect( @conn.wait_for_notify( 10 ) ).to eq( 'woo' )
|
377
372
|
@conn.exec( 'UNLISTEN woo' )
|
378
373
|
|
379
374
|
t.join
|
@@ -395,8 +390,8 @@ describe PG::Connection do
|
|
395
390
|
|
396
391
|
eventpid = event = nil
|
397
392
|
@conn.wait_for_notify( 10 ) {|*args| event, eventpid = args }
|
398
|
-
event.
|
399
|
-
eventpid.
|
393
|
+
expect( event ).to eq( 'woo' )
|
394
|
+
expect( eventpid ).to be_an( Integer )
|
400
395
|
|
401
396
|
@conn.exec( 'UNLISTEN woo' )
|
402
397
|
|
@@ -423,8 +418,8 @@ describe PG::Connection do
|
|
423
418
|
channels << @conn.wait_for_notify( 2 )
|
424
419
|
end
|
425
420
|
|
426
|
-
channels.
|
427
|
-
channels.
|
421
|
+
expect( channels.size ).to eq( 3 )
|
422
|
+
expect( channels ).to include( 'woo', 'war', 'woz' )
|
428
423
|
|
429
424
|
@conn.exec( 'UNLISTEN woz' )
|
430
425
|
@conn.exec( 'UNLISTEN war' )
|
@@ -446,7 +441,7 @@ describe PG::Connection do
|
|
446
441
|
# Cause the notification to buffer, but not be read yet
|
447
442
|
@conn.exec( 'SELECT 1' )
|
448
443
|
|
449
|
-
@conn.wait_for_notify( 10 ).
|
444
|
+
expect( @conn.wait_for_notify( 10 ) ).to eq( 'woo' )
|
450
445
|
@conn.exec( 'UNLISTEN woo' )
|
451
446
|
end
|
452
447
|
|
@@ -457,41 +452,41 @@ describe PG::Connection do
|
|
457
452
|
end
|
458
453
|
st = Time.now
|
459
454
|
@conn.send_query "SELECT pg_sleep(0.5); do $$ BEGIN RAISE NOTICE 'woohoo'; END; $$ LANGUAGE plpgsql;"
|
460
|
-
@conn.wait_for_notify( 1 ).
|
461
|
-
notices.first.
|
455
|
+
expect( @conn.wait_for_notify( 1 ) ).to be_nil
|
456
|
+
expect( notices.first ).to_not be_nil
|
462
457
|
et = Time.now
|
463
|
-
(et - notices.first[1]).
|
464
|
-
(et - st).
|
465
|
-
(et - st).
|
458
|
+
expect( (et - notices.first[1]) ).to be >= 0.4
|
459
|
+
expect( (et - st) ).to be >= 0.9
|
460
|
+
expect( (et - st) ).to be < 1.4
|
466
461
|
end
|
467
462
|
|
468
463
|
it "yields the result if block is given to exec" do
|
469
464
|
rval = @conn.exec( "select 1234::int as a union select 5678::int as a" ) do |result|
|
470
465
|
values = []
|
471
|
-
result.
|
472
|
-
result.ntuples.
|
466
|
+
expect( result ).to be_kind_of( PG::Result )
|
467
|
+
expect( result.ntuples ).to eq( 2 )
|
473
468
|
result.each do |tuple|
|
474
469
|
values << tuple['a']
|
475
470
|
end
|
476
471
|
values
|
477
472
|
end
|
478
473
|
|
479
|
-
rval.
|
480
|
-
rval.
|
474
|
+
expect( rval.size ).to eq( 2 )
|
475
|
+
expect( rval ).to include( '5678', '1234' )
|
481
476
|
end
|
482
477
|
|
483
478
|
it "can process #copy_data output queries" do
|
484
479
|
rows = []
|
485
480
|
res2 = @conn.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" ) do |res|
|
486
|
-
res.result_status.
|
487
|
-
res.nfields.
|
481
|
+
expect( res.result_status ).to eq( PG::PGRES_COPY_OUT )
|
482
|
+
expect( res.nfields ).to eq( 1 )
|
488
483
|
while row=@conn.get_copy_data
|
489
484
|
rows << row
|
490
485
|
end
|
491
486
|
end
|
492
|
-
rows.
|
493
|
-
res2.result_status.
|
494
|
-
|
487
|
+
expect( rows ).to eq( ["1\n", "2\n"] )
|
488
|
+
expect( res2.result_status ).to eq( PG::PGRES_COMMAND_OK )
|
489
|
+
expect( @conn ).to still_be_usable
|
495
490
|
end
|
496
491
|
|
497
492
|
it "can handle incomplete #copy_data output queries" do
|
@@ -500,7 +495,7 @@ describe PG::Connection do
|
|
500
495
|
@conn.get_copy_data
|
501
496
|
end
|
502
497
|
}.to raise_error(PG::NotAllCopyDataRetrieved, /Not all/)
|
503
|
-
|
498
|
+
expect( @conn ).to still_be_usable
|
504
499
|
end
|
505
500
|
|
506
501
|
it "can handle client errors in #copy_data for output" do
|
@@ -509,10 +504,10 @@ describe PG::Connection do
|
|
509
504
|
raise "boom"
|
510
505
|
end
|
511
506
|
}.to raise_error(RuntimeError, "boom")
|
512
|
-
|
507
|
+
expect( @conn ).to still_be_usable
|
513
508
|
end
|
514
509
|
|
515
|
-
it "can handle server errors in #copy_data for output" do
|
510
|
+
it "can handle server errors in #copy_data for output", :postgresql_90 do
|
516
511
|
@conn.exec "ROLLBACK"
|
517
512
|
@conn.transaction do
|
518
513
|
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
@@ -523,23 +518,23 @@ describe PG::Connection do
|
|
523
518
|
end
|
524
519
|
}.to raise_error(PG::Error, /test-error/)
|
525
520
|
end
|
526
|
-
|
521
|
+
expect( @conn ).to still_be_usable
|
527
522
|
end
|
528
523
|
|
529
524
|
it "can process #copy_data input queries" do
|
530
525
|
@conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
531
526
|
res2 = @conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
532
|
-
res.result_status.
|
533
|
-
res.nfields.
|
527
|
+
expect( res.result_status ).to eq( PG::PGRES_COPY_IN )
|
528
|
+
expect( res.nfields ).to eq( 1 )
|
534
529
|
@conn.put_copy_data "1\n"
|
535
530
|
@conn.put_copy_data "2\n"
|
536
531
|
end
|
537
|
-
res2.result_status.
|
532
|
+
expect( res2.result_status ).to eq( PG::PGRES_COMMAND_OK )
|
538
533
|
|
539
|
-
|
534
|
+
expect( @conn ).to still_be_usable
|
540
535
|
|
541
536
|
res = @conn.exec( "SELECT * FROM copytable ORDER BY col1" )
|
542
|
-
res.values.
|
537
|
+
expect( res.values ).to eq( [["1"], ["2"]] )
|
543
538
|
end
|
544
539
|
|
545
540
|
it "can handle client errors in #copy_data for input" do
|
@@ -552,7 +547,8 @@ describe PG::Connection do
|
|
552
547
|
end
|
553
548
|
}.to raise_error(RuntimeError, "boom")
|
554
549
|
end
|
555
|
-
|
550
|
+
|
551
|
+
expect( @conn ).to still_be_usable
|
556
552
|
end
|
557
553
|
|
558
554
|
it "can handle server errors in #copy_data for input" do
|
@@ -565,7 +561,7 @@ describe PG::Connection do
|
|
565
561
|
end
|
566
562
|
}.to raise_error(PG::Error, /invalid input syntax for integer/)
|
567
563
|
end
|
568
|
-
|
564
|
+
expect( @conn ).to still_be_usable
|
569
565
|
end
|
570
566
|
|
571
567
|
it "should raise an error for non copy statements in #copy_data" do
|
@@ -573,7 +569,7 @@ describe PG::Connection do
|
|
573
569
|
@conn.copy_data( "SELECT 1" ){}
|
574
570
|
}.to raise_error(ArgumentError, /no COPY/)
|
575
571
|
|
576
|
-
|
572
|
+
expect( @conn ).to still_be_usable
|
577
573
|
end
|
578
574
|
|
579
575
|
it "correctly finishes COPY queries passed to #async_exec" do
|
@@ -589,8 +585,8 @@ describe PG::Connection do
|
|
589
585
|
results << data if data
|
590
586
|
end until data.nil?
|
591
587
|
|
592
|
-
results.
|
593
|
-
results.
|
588
|
+
expect( results.size ).to eq( 2 )
|
589
|
+
expect( results ).to include( "1\n", "2\n" )
|
594
590
|
end
|
595
591
|
|
596
592
|
|
@@ -602,10 +598,10 @@ describe PG::Connection do
|
|
602
598
|
end
|
603
599
|
|
604
600
|
sleep 0.5
|
605
|
-
t.
|
601
|
+
expect( t ).to be_alive()
|
606
602
|
@conn.cancel
|
607
603
|
t.join
|
608
|
-
(Time.now - start).
|
604
|
+
expect( (Time.now - start) ).to be < 3
|
609
605
|
end
|
610
606
|
|
611
607
|
it "described_class#block should allow a timeout" do
|
@@ -615,13 +611,49 @@ describe PG::Connection do
|
|
615
611
|
@conn.block( 0.1 )
|
616
612
|
finish = Time.now
|
617
613
|
|
618
|
-
(finish - start).
|
614
|
+
expect( (finish - start) ).to be_within( 0.05 ).of( 0.1 )
|
619
615
|
end
|
620
616
|
|
621
617
|
|
622
618
|
it "can encrypt a string given a password and username" do
|
623
|
-
described_class.encrypt_password("postgres", "postgres").
|
624
|
-
|
619
|
+
expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
620
|
+
end
|
621
|
+
|
622
|
+
it "can return the default connection options" do
|
623
|
+
expect( described_class.conndefaults ).to be_a( Array )
|
624
|
+
expect( described_class.conndefaults ).to all( be_a(Hash) )
|
625
|
+
expect( described_class.conndefaults[0] ).to include( :keyword, :label, :dispchar, :dispsize )
|
626
|
+
expect( @conn.conndefaults ).to eq( described_class.conndefaults )
|
627
|
+
end
|
628
|
+
|
629
|
+
it "can return the default connection options as a Hash" do
|
630
|
+
expect( described_class.conndefaults_hash ).to be_a( Hash )
|
631
|
+
expect( described_class.conndefaults_hash ).to include( :user, :password, :dbname, :host, :port )
|
632
|
+
expect( described_class.conndefaults_hash[:port] ).to eq( '54321' )
|
633
|
+
expect( @conn.conndefaults_hash ).to eq( described_class.conndefaults_hash )
|
634
|
+
end
|
635
|
+
|
636
|
+
it "can return the connection's connection options", :postgresql_93 do
|
637
|
+
expect( @conn.conninfo ).to be_a( Array )
|
638
|
+
expect( @conn.conninfo ).to all( be_a(Hash) )
|
639
|
+
expect( @conn.conninfo[0] ).to include( :keyword, :label, :dispchar, :dispsize )
|
640
|
+
end
|
641
|
+
|
642
|
+
|
643
|
+
it "can return the connection's connection options as a Hash", :postgresql_93 do
|
644
|
+
expect( @conn.conninfo_hash ).to be_a( Hash )
|
645
|
+
expect( @conn.conninfo_hash ).to include( :user, :password, :connect_timeout, :dbname, :host )
|
646
|
+
expect( @conn.conninfo_hash[:dbname] ).to eq( 'test' )
|
647
|
+
end
|
648
|
+
|
649
|
+
|
650
|
+
it "honors the connect_timeout connection parameter", :postgresql_93 do
|
651
|
+
conn = PG.connect( port: @port, dbname: 'test', connect_timeout: 11 )
|
652
|
+
begin
|
653
|
+
expect( conn.conninfo_hash[:connect_timeout] ).to eq( "11" )
|
654
|
+
ensure
|
655
|
+
conn.finish
|
656
|
+
end
|
625
657
|
end
|
626
658
|
|
627
659
|
|
@@ -641,15 +673,15 @@ describe PG::Connection do
|
|
641
673
|
|
642
674
|
it "allows fetching a column of values from a result by column number" do
|
643
675
|
res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
|
644
|
-
res.column_values( 0 ).
|
645
|
-
res.column_values( 1 ).
|
676
|
+
expect( res.column_values( 0 ) ).to eq( %w[1 2 3] )
|
677
|
+
expect( res.column_values( 1 ) ).to eq( %w[2 3 4] )
|
646
678
|
end
|
647
679
|
|
648
680
|
|
649
681
|
it "allows fetching a column of values from a result by field name" do
|
650
682
|
res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
|
651
|
-
res.field_values( 'column1' ).
|
652
|
-
res.field_values( 'column2' ).
|
683
|
+
expect( res.field_values( 'column1' ) ).to eq( %w[1 2 3] )
|
684
|
+
expect( res.field_values( 'column2' ) ).to eq( %w[2 3 4] )
|
653
685
|
end
|
654
686
|
|
655
687
|
|
@@ -680,13 +712,13 @@ describe PG::Connection do
|
|
680
712
|
it "can connect asynchronously", :socket_io do
|
681
713
|
serv = TCPServer.new( '127.0.0.1', 54320 )
|
682
714
|
conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
683
|
-
[PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK].
|
715
|
+
expect( [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK] ).to include conn.connect_poll
|
684
716
|
select( nil, [conn.socket_io], nil, 0.2 )
|
685
717
|
serv.close
|
686
718
|
if conn.connect_poll == PG::PGRES_POLLING_READING
|
687
719
|
select( [conn.socket_io], nil, nil, 0.2 )
|
688
720
|
end
|
689
|
-
conn.connect_poll.
|
721
|
+
expect( conn.connect_poll ).to eq( PG::PGRES_POLLING_FAILED )
|
690
722
|
end
|
691
723
|
|
692
724
|
it "discards previous results (if any) before waiting on an #async_exec"
|
@@ -696,7 +728,7 @@ describe PG::Connection do
|
|
696
728
|
@conn.async_exec( "select 47 as one" ) do |pg_res|
|
697
729
|
result = pg_res[0]
|
698
730
|
end
|
699
|
-
result.
|
731
|
+
expect( result ).to eq( { 'one' => '47' } )
|
700
732
|
end
|
701
733
|
|
702
734
|
it "raises a rescue-able error if #finish is called twice", :without_transaction do
|
@@ -710,7 +742,7 @@ describe PG::Connection do
|
|
710
742
|
conn = PG.connect( @conninfo )
|
711
743
|
io = conn.socket_io
|
712
744
|
conn.finish
|
713
|
-
io.
|
745
|
+
expect( io ).to be_closed()
|
714
746
|
expect { conn.socket_io }.to raise_error( PG::ConnectionBad, /connection is closed/i )
|
715
747
|
end
|
716
748
|
|
@@ -718,8 +750,8 @@ describe PG::Connection do
|
|
718
750
|
conn = PG.connect( @conninfo )
|
719
751
|
io = conn.socket_io
|
720
752
|
conn.reset
|
721
|
-
io.
|
722
|
-
conn.socket_io.
|
753
|
+
expect( io ).to be_closed()
|
754
|
+
expect( conn.socket_io ).to_not equal( io )
|
723
755
|
conn.finish
|
724
756
|
end
|
725
757
|
|
@@ -742,7 +774,11 @@ describe PG::Connection do
|
|
742
774
|
|
743
775
|
it "sets the fallback_application_name on new connections" do
|
744
776
|
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
745
|
-
|
777
|
+
|
778
|
+
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
779
|
+
expect( conn_name ).to include( $0[0..10] )
|
780
|
+
expect( conn_name ).to include( $0[-10..-1] )
|
781
|
+
expect( conn_name.length ).to be <= 64
|
746
782
|
end
|
747
783
|
|
748
784
|
it "sets a shortened fallback_application_name on new connections" do
|
@@ -750,7 +786,10 @@ describe PG::Connection do
|
|
750
786
|
begin
|
751
787
|
$0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
|
752
788
|
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
753
|
-
|
789
|
+
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
790
|
+
expect( conn_name ).to include( $0[0..10] )
|
791
|
+
expect( conn_name ).to include( $0[-10..-1] )
|
792
|
+
expect( conn_name.length ).to be <= 64
|
754
793
|
ensure
|
755
794
|
$0 = old_0
|
756
795
|
end
|
@@ -772,9 +811,9 @@ describe PG::Connection do
|
|
772
811
|
end
|
773
812
|
@conn.exec( 'UNLISTEN knees' )
|
774
813
|
|
775
|
-
event.
|
776
|
-
pid.
|
777
|
-
msg.
|
814
|
+
expect( event ).to eq( 'knees' )
|
815
|
+
expect( pid ).to be_a_kind_of( Integer )
|
816
|
+
expect( msg ).to eq( 'skirt and boots' )
|
778
817
|
end
|
779
818
|
|
780
819
|
it "accepts nil as the timeout in #wait_for_notify " do
|
@@ -791,8 +830,8 @@ describe PG::Connection do
|
|
791
830
|
end
|
792
831
|
@conn.exec( 'UNLISTEN knees' )
|
793
832
|
|
794
|
-
event.
|
795
|
-
pid.
|
833
|
+
expect( event ).to eq( 'knees' )
|
834
|
+
expect( pid ).to be_a_kind_of( Integer )
|
796
835
|
end
|
797
836
|
|
798
837
|
it "sends nil as the payload if the notification wasn't given one" do
|
@@ -809,7 +848,7 @@ describe PG::Connection do
|
|
809
848
|
end
|
810
849
|
@conn.exec( 'UNLISTEN knees' )
|
811
850
|
|
812
|
-
payload.
|
851
|
+
expect( payload ).to be_nil()
|
813
852
|
end
|
814
853
|
|
815
854
|
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
@@ -828,9 +867,9 @@ describe PG::Connection do
|
|
828
867
|
end
|
829
868
|
@conn.exec( 'UNLISTEN knees' )
|
830
869
|
|
831
|
-
event.
|
832
|
-
pid.
|
833
|
-
msg.
|
870
|
+
expect( event ).to eq( 'knees' )
|
871
|
+
expect( pid ).to be_a_kind_of( Integer )
|
872
|
+
expect( msg ).to be_nil()
|
834
873
|
end
|
835
874
|
|
836
875
|
it "calls the block supplied to wait_for_notify with the notify payload if it " +
|
@@ -849,7 +888,7 @@ describe PG::Connection do
|
|
849
888
|
end
|
850
889
|
@conn.exec( 'UNLISTEN knees' )
|
851
890
|
|
852
|
-
notification_received.
|
891
|
+
expect( notification_received ).to be_truthy()
|
853
892
|
end
|
854
893
|
|
855
894
|
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
@@ -868,9 +907,9 @@ describe PG::Connection do
|
|
868
907
|
end
|
869
908
|
@conn.exec( 'UNLISTEN knees' )
|
870
909
|
|
871
|
-
event.
|
872
|
-
pid.
|
873
|
-
msg.
|
910
|
+
expect( event ).to eq( 'knees' )
|
911
|
+
expect( pid ).to be_a_kind_of( Integer )
|
912
|
+
expect( msg ).to eq( 'skirt and boots' )
|
874
913
|
end
|
875
914
|
|
876
915
|
end
|
@@ -879,12 +918,12 @@ describe PG::Connection do
|
|
879
918
|
|
880
919
|
it "pings successfully with connection string" do
|
881
920
|
ping = described_class.ping(@conninfo)
|
882
|
-
ping.
|
921
|
+
expect( ping ).to eq( PG::PQPING_OK )
|
883
922
|
end
|
884
923
|
|
885
924
|
it "pings using 7 arguments converted to strings" do
|
886
925
|
ping = described_class.ping('localhost', @port, nil, nil, :test, nil, nil)
|
887
|
-
ping.
|
926
|
+
expect( ping ).to eq( PG::PQPING_OK )
|
888
927
|
end
|
889
928
|
|
890
929
|
it "pings using a hash of connection parameters" do
|
@@ -892,7 +931,7 @@ describe PG::Connection do
|
|
892
931
|
:host => 'localhost',
|
893
932
|
:port => @port,
|
894
933
|
:dbname => :test)
|
895
|
-
ping.
|
934
|
+
expect( ping ).to eq( PG::PQPING_OK )
|
896
935
|
end
|
897
936
|
|
898
937
|
it "returns correct response when ping connection cannot be established" do
|
@@ -900,12 +939,12 @@ describe PG::Connection do
|
|
900
939
|
:host => 'localhost',
|
901
940
|
:port => 9999,
|
902
941
|
:dbname => :test)
|
903
|
-
ping.
|
942
|
+
expect( ping ).to eq( PG::PQPING_NO_RESPONSE )
|
904
943
|
end
|
905
944
|
|
906
945
|
it "returns correct response when ping connection arguments are wrong" do
|
907
946
|
ping = described_class.ping('localhost', 'localhost', nil, nil, :test, nil, nil)
|
908
|
-
ping.
|
947
|
+
expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
|
909
948
|
end
|
910
949
|
|
911
950
|
|
@@ -930,15 +969,15 @@ describe PG::Connection do
|
|
930
969
|
res = @conn.get_result or break
|
931
970
|
results << res
|
932
971
|
end
|
933
|
-
results.length.
|
972
|
+
expect( results.length ).to eq( 11 )
|
934
973
|
results[0..-2].each do |res|
|
935
|
-
res.result_status.
|
974
|
+
expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
936
975
|
values = res.field_values('generate_series')
|
937
|
-
values.length.
|
938
|
-
values.first.to_i.
|
976
|
+
expect( values.length ).to eq( 1 )
|
977
|
+
expect( values.first.to_i ).to be > 0
|
939
978
|
end
|
940
|
-
results.last.result_status.
|
941
|
-
results.last.ntuples.
|
979
|
+
expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
|
980
|
+
expect( results.last.ntuples ).to eq( 0 )
|
942
981
|
end
|
943
982
|
|
944
983
|
it "should receive rows before entire query is finished" do
|
@@ -952,8 +991,8 @@ describe PG::Connection do
|
|
952
991
|
res.check
|
953
992
|
first_row_time = Time.now unless first_row_time
|
954
993
|
end
|
955
|
-
(Time.now - start_time).
|
956
|
-
(first_row_time - start_time).
|
994
|
+
expect( (Time.now - start_time) ).to be >= 1.0
|
995
|
+
expect( (first_row_time - start_time) ).to be < 1.0
|
957
996
|
end
|
958
997
|
|
959
998
|
it "should receive rows before entire query fails" do
|
@@ -969,8 +1008,8 @@ describe PG::Connection do
|
|
969
1008
|
first_result ||= res
|
970
1009
|
end
|
971
1010
|
end.to raise_error(PG::Error)
|
972
|
-
first_result.kind_of?(PG::Result).
|
973
|
-
first_result.result_status.
|
1011
|
+
expect( first_result.kind_of?(PG::Result) ).to be_truthy
|
1012
|
+
expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
974
1013
|
end
|
975
1014
|
end
|
976
1015
|
end
|
@@ -985,8 +1024,8 @@ describe PG::Connection do
|
|
985
1024
|
res = conn.exec("VALUES ('fantasia')", [], 0)
|
986
1025
|
out_string = res[0]['column1']
|
987
1026
|
end
|
988
|
-
out_string.
|
989
|
-
out_string.encoding.
|
1027
|
+
expect( out_string ).to eq( 'fantasia' )
|
1028
|
+
expect( out_string.encoding ).to eq( Encoding::ISO8859_1 )
|
990
1029
|
end
|
991
1030
|
|
992
1031
|
it "should return results in the same encoding as the client (utf-8)" do
|
@@ -996,8 +1035,8 @@ describe PG::Connection do
|
|
996
1035
|
res = conn.exec("VALUES ('世界線航跡蔵')", [], 0)
|
997
1036
|
out_string = res[0]['column1']
|
998
1037
|
end
|
999
|
-
out_string.
|
1000
|
-
out_string.encoding.
|
1038
|
+
expect( out_string ).to eq( '世界線航跡蔵' )
|
1039
|
+
expect( out_string.encoding ).to eq( Encoding::UTF_8 )
|
1001
1040
|
end
|
1002
1041
|
|
1003
1042
|
it "should return results in the same encoding as the client (EUC-JP)" do
|
@@ -1008,8 +1047,8 @@ describe PG::Connection do
|
|
1008
1047
|
res = conn.exec(stmt, [], 0)
|
1009
1048
|
out_string = res[0]['column1']
|
1010
1049
|
end
|
1011
|
-
out_string.
|
1012
|
-
out_string.encoding.
|
1050
|
+
expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
|
1051
|
+
expect( out_string.encoding ).to eq( Encoding::EUC_JP )
|
1013
1052
|
end
|
1014
1053
|
|
1015
1054
|
it "returns the results in the correct encoding even if the client_encoding has " +
|
@@ -1022,75 +1061,61 @@ describe PG::Connection do
|
|
1022
1061
|
conn.internal_encoding = 'utf-8'
|
1023
1062
|
out_string = res[0]['column1']
|
1024
1063
|
end
|
1025
|
-
out_string.
|
1026
|
-
out_string.encoding.
|
1064
|
+
expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
|
1065
|
+
expect( out_string.encoding ).to eq( Encoding::EUC_JP )
|
1027
1066
|
end
|
1028
1067
|
|
1029
1068
|
it "the connection should return ASCII-8BIT when it's set to SQL_ASCII" do
|
1030
1069
|
@conn.exec "SET client_encoding TO SQL_ASCII"
|
1031
|
-
@conn.internal_encoding.
|
1032
|
-
end
|
1033
|
-
|
1034
|
-
it "works around the unsupported JOHAB encoding by returning stuff in 'ASCII_8BIT'" do
|
1035
|
-
pending "figuring out how to create a string in the JOHAB encoding" do
|
1036
|
-
out_string = nil
|
1037
|
-
@conn.transaction do |conn|
|
1038
|
-
conn.exec( "set client_encoding = 'JOHAB';" )
|
1039
|
-
stmt = "VALUES ('foo')".encode('JOHAB')
|
1040
|
-
res = conn.exec( stmt, [], 0 )
|
1041
|
-
out_string = res[0]['column1']
|
1042
|
-
end
|
1043
|
-
out_string.should == 'foo'.encode( Encoding::ASCII_8BIT )
|
1044
|
-
out_string.encoding.should == Encoding::ASCII_8BIT
|
1045
|
-
end
|
1070
|
+
expect( @conn.internal_encoding ).to eq( Encoding::ASCII_8BIT )
|
1046
1071
|
end
|
1047
1072
|
|
1048
1073
|
it "uses the client encoding for escaped string" do
|
1049
1074
|
original = "string to\0 escape".force_encoding( "iso8859-1" )
|
1050
1075
|
@conn.set_client_encoding( "euc_jp" )
|
1051
1076
|
escaped = @conn.escape( original )
|
1052
|
-
escaped.encoding.
|
1053
|
-
escaped.
|
1077
|
+
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1078
|
+
expect( escaped ).to eq( "string to" )
|
1054
1079
|
end
|
1055
1080
|
|
1056
1081
|
it "uses the client encoding for escaped literal", :postgresql_90 do
|
1057
1082
|
original = "string to\0 escape".force_encoding( "iso8859-1" )
|
1058
1083
|
@conn.set_client_encoding( "euc_jp" )
|
1059
1084
|
escaped = @conn.escape_literal( original )
|
1060
|
-
escaped.encoding.
|
1061
|
-
escaped.
|
1085
|
+
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1086
|
+
expect( escaped ).to eq( "'string to'" )
|
1062
1087
|
end
|
1063
1088
|
|
1064
1089
|
it "uses the client encoding for escaped identifier", :postgresql_90 do
|
1065
1090
|
original = "string to\0 escape".force_encoding( "iso8859-1" )
|
1066
1091
|
@conn.set_client_encoding( "euc_jp" )
|
1067
1092
|
escaped = @conn.escape_identifier( original )
|
1068
|
-
escaped.encoding.
|
1069
|
-
escaped.
|
1093
|
+
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1094
|
+
expect( escaped ).to eq( "\"string to\"" )
|
1070
1095
|
end
|
1071
1096
|
|
1072
1097
|
it "uses the client encoding for quote_ident" do
|
1073
1098
|
original = "string to\0 escape".force_encoding( "iso8859-1" )
|
1074
1099
|
@conn.set_client_encoding( "euc_jp" )
|
1075
1100
|
escaped = @conn.quote_ident( original )
|
1076
|
-
escaped.encoding.
|
1077
|
-
escaped.
|
1101
|
+
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1102
|
+
expect( escaped ).to eq( "\"string to\"" )
|
1078
1103
|
end
|
1079
1104
|
|
1080
1105
|
it "uses the previous string encoding for escaped string" do
|
1081
1106
|
original = "string to\0 escape".force_encoding( "iso8859-1" )
|
1082
1107
|
@conn.set_client_encoding( "euc_jp" )
|
1083
1108
|
escaped = described_class.escape( original )
|
1084
|
-
escaped.encoding.
|
1085
|
-
escaped.
|
1109
|
+
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1110
|
+
expect( escaped ).to eq( "string to" )
|
1086
1111
|
end
|
1087
1112
|
|
1088
1113
|
it "uses the previous string encoding for quote_ident" do
|
1089
1114
|
original = "string to\0 escape".force_encoding( "iso8859-1" )
|
1090
1115
|
@conn.set_client_encoding( "euc_jp" )
|
1091
1116
|
escaped = described_class.quote_ident( original )
|
1092
|
-
escaped.encoding.
|
1093
|
-
escaped.
|
1117
|
+
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1118
|
+
expect( escaped ).to eq( "\"string to\"" )
|
1094
1119
|
end
|
1095
1120
|
|
1096
1121
|
end
|
@@ -1110,10 +1135,11 @@ describe PG::Connection do
|
|
1110
1135
|
Encoding.default_internal = Encoding::UTF_8
|
1111
1136
|
|
1112
1137
|
conn = PG.connect( @conninfo )
|
1113
|
-
conn.internal_encoding.
|
1138
|
+
expect( conn.internal_encoding ).to eq( Encoding::UTF_8 )
|
1114
1139
|
res = conn.exec( "SELECT foo FROM defaultinternaltest" )
|
1115
|
-
res[0]['foo'].encoding.
|
1140
|
+
expect( res[0]['foo'].encoding ).to eq( Encoding::UTF_8 )
|
1116
1141
|
ensure
|
1142
|
+
conn.exec( "DROP TABLE defaultinternaltest" )
|
1117
1143
|
conn.finish if conn
|
1118
1144
|
Encoding.default_internal = prev_encoding
|
1119
1145
|
end
|
@@ -1126,7 +1152,7 @@ describe PG::Connection do
|
|
1126
1152
|
|
1127
1153
|
@conn.set_default_encoding
|
1128
1154
|
|
1129
|
-
@conn.internal_encoding.
|
1155
|
+
expect( @conn.internal_encoding ).to eq( Encoding::KOI8_R )
|
1130
1156
|
ensure
|
1131
1157
|
Encoding.default_internal = prev_encoding
|
1132
1158
|
end
|
@@ -1147,7 +1173,7 @@ describe PG::Connection do
|
|
1147
1173
|
query = "INSERT INTO foo VALUES ('Côte d'Ivoire')".encode( 'iso-8859-15' )
|
1148
1174
|
conn.exec( query )
|
1149
1175
|
rescue => err
|
1150
|
-
err.message.encoding.
|
1176
|
+
expect( err.message.encoding ).to eq( Encoding::ISO8859_15 )
|
1151
1177
|
else
|
1152
1178
|
fail "No exception raised?!"
|
1153
1179
|
end
|
@@ -1156,6 +1182,21 @@ describe PG::Connection do
|
|
1156
1182
|
conn.finish if conn
|
1157
1183
|
end
|
1158
1184
|
|
1185
|
+
it "handles clearing result in or after set_notice_receiver", :postgresql_90 do
|
1186
|
+
r = nil
|
1187
|
+
@conn.set_notice_receiver do |result|
|
1188
|
+
r = result
|
1189
|
+
expect( r.cleared? ).to eq(false)
|
1190
|
+
end
|
1191
|
+
@conn.exec "do $$ BEGIN RAISE NOTICE 'foo'; END; $$ LANGUAGE plpgsql;"
|
1192
|
+
sleep 0.2
|
1193
|
+
expect( r ).to be_a( PG::Result )
|
1194
|
+
expect( r.cleared? ).to eq(true)
|
1195
|
+
expect( r.autoclear? ).to eq(true)
|
1196
|
+
r.clear
|
1197
|
+
@conn.set_notice_receiver
|
1198
|
+
end
|
1199
|
+
|
1159
1200
|
it "receives properly encoded messages in the notice callbacks", :postgresql_90 do
|
1160
1201
|
[:receiver, :processor].each do |kind|
|
1161
1202
|
notices = []
|
@@ -1174,10 +1215,10 @@ describe PG::Connection do
|
|
1174
1215
|
@conn.exec "do $$ BEGIN RAISE NOTICE '世界線航跡蔵'; END; $$ LANGUAGE plpgsql;"
|
1175
1216
|
end
|
1176
1217
|
|
1177
|
-
notices.length.
|
1218
|
+
expect( notices.length ).to eq( 3 )
|
1178
1219
|
notices.each do |notice|
|
1179
|
-
notice.
|
1180
|
-
notice.encoding.
|
1220
|
+
expect( notice ).to match( /^NOTICE:.*世界線航跡蔵/ )
|
1221
|
+
expect( notice.encoding ).to eq( Encoding::UTF_8 )
|
1181
1222
|
end
|
1182
1223
|
@conn.set_notice_receiver
|
1183
1224
|
@conn.set_notice_processor
|
@@ -1195,10 +1236,10 @@ describe PG::Connection do
|
|
1195
1236
|
end
|
1196
1237
|
@conn.exec( 'UNLISTEN "Möhre"' )
|
1197
1238
|
|
1198
|
-
event.
|
1199
|
-
event.encoding.
|
1200
|
-
msg.
|
1201
|
-
msg.encoding.
|
1239
|
+
expect( event ).to eq( "Möhre" )
|
1240
|
+
expect( event.encoding ).to eq( Encoding::UTF_8 )
|
1241
|
+
expect( msg ).to eq( '世界線航跡蔵' )
|
1242
|
+
expect( msg.encoding ).to eq( Encoding::UTF_8 )
|
1202
1243
|
end
|
1203
1244
|
|
1204
1245
|
it "returns properly encoded text from notifies", :postgresql_90 do
|
@@ -1209,11 +1250,11 @@ describe PG::Connection do
|
|
1209
1250
|
@conn.exec( 'UNLISTEN "Möhre"' )
|
1210
1251
|
|
1211
1252
|
notification = @conn.notifies
|
1212
|
-
notification[:relname].
|
1213
|
-
notification[:relname].encoding.
|
1214
|
-
notification[:extra].
|
1215
|
-
notification[:extra].encoding.
|
1216
|
-
notification[:be_pid].
|
1253
|
+
expect( notification[:relname] ).to eq( "Möhre" )
|
1254
|
+
expect( notification[:relname].encoding ).to eq( Encoding::UTF_8 )
|
1255
|
+
expect( notification[:extra] ).to eq( '世界線航跡蔵' )
|
1256
|
+
expect( notification[:extra].encoding ).to eq( Encoding::UTF_8 )
|
1257
|
+
expect( notification[:be_pid] ).to be > 0
|
1217
1258
|
end
|
1218
1259
|
end
|
1219
1260
|
|
@@ -1224,7 +1265,7 @@ describe PG::Connection do
|
|
1224
1265
|
end
|
1225
1266
|
|
1226
1267
|
sleep 0.5
|
1227
|
-
t.
|
1268
|
+
expect( t ).to be_alive()
|
1228
1269
|
t.join
|
1229
1270
|
end
|
1230
1271
|
|
@@ -1238,9 +1279,170 @@ describe PG::Connection do
|
|
1238
1279
|
end
|
1239
1280
|
|
1240
1281
|
sleep 0.5
|
1241
|
-
t.
|
1282
|
+
expect( t ).to be_alive()
|
1242
1283
|
serv.close
|
1243
1284
|
t.join
|
1244
1285
|
end
|
1245
1286
|
end
|
1287
|
+
|
1288
|
+
describe "type casting" do
|
1289
|
+
it "should raise an error on invalid param mapping" do
|
1290
|
+
expect{
|
1291
|
+
@conn.exec_params( "SELECT 1", [], nil, :invalid )
|
1292
|
+
}.to raise_error(TypeError)
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
it "should return nil if no type mapping is set" do
|
1296
|
+
expect( @conn.type_map_for_queries ).to be_nil
|
1297
|
+
expect( @conn.type_map_for_results ).to be_nil
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
it "shouldn't type map params unless requested" do
|
1301
|
+
expect{
|
1302
|
+
@conn.exec_params( "SELECT $1", [5] )
|
1303
|
+
}.to raise_error(PG::IndeterminateDatatype)
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
it "should raise an error on invalid encoder to put_copy_data" do
|
1307
|
+
expect{
|
1308
|
+
@conn.put_copy_data [1], :invalid
|
1309
|
+
}.to raise_error(TypeError)
|
1310
|
+
end
|
1311
|
+
|
1312
|
+
it "can type cast parameters to put_copy_data with explicit encoder" do
|
1313
|
+
tm = PG::TypeMapByColumn.new [nil]
|
1314
|
+
row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
|
1315
|
+
|
1316
|
+
@conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
1317
|
+
res2 = @conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
1318
|
+
@conn.put_copy_data [1], row_encoder
|
1319
|
+
@conn.put_copy_data ["2"], row_encoder
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
res2 = @conn.copy_data( "COPY copytable FROM STDOUT", row_encoder ) do |res|
|
1323
|
+
@conn.put_copy_data [3]
|
1324
|
+
@conn.put_copy_data ["4"]
|
1325
|
+
end
|
1326
|
+
|
1327
|
+
res = @conn.exec( "SELECT * FROM copytable ORDER BY col1" )
|
1328
|
+
expect( res.values ).to eq( [["1"], ["2"], ["3"], ["4"]] )
|
1329
|
+
end
|
1330
|
+
|
1331
|
+
context "with default query type map" do
|
1332
|
+
before :each do
|
1333
|
+
@conn2 = described_class.new(@conninfo)
|
1334
|
+
tm = PG::TypeMapByMriType.new
|
1335
|
+
tm['T_FIXNUM'] = PG::TextEncoder::Integer.new oid: 20
|
1336
|
+
@conn2.type_map_for_queries = tm
|
1337
|
+
|
1338
|
+
row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
|
1339
|
+
@conn2.encoder_for_put_copy_data = row_encoder
|
1340
|
+
end
|
1341
|
+
after :each do
|
1342
|
+
@conn2.close
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
it "should respect a type mapping for params and it's OID and format code" do
|
1346
|
+
res = @conn2.exec_params( "SELECT $1", [5] )
|
1347
|
+
expect( res.values ).to eq( [["5"]] )
|
1348
|
+
expect( res.ftype(0) ).to eq( 20 )
|
1349
|
+
end
|
1350
|
+
|
1351
|
+
it "should return the current type mapping" do
|
1352
|
+
expect( @conn2.type_map_for_queries ).to be_kind_of(PG::TypeMapByMriType)
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
it "should work with arbitrary number of params in conjunction with type casting" do
|
1356
|
+
begin
|
1357
|
+
3.step( 12, 0.2 ) do |exp|
|
1358
|
+
num_params = (2 ** exp).to_i
|
1359
|
+
sql = num_params.times.map{|n| "$#{n+1}" }.join(",")
|
1360
|
+
params = num_params.times.to_a
|
1361
|
+
res = @conn2.exec_params( "SELECT #{sql}", params )
|
1362
|
+
expect( res.nfields ).to eq( num_params )
|
1363
|
+
expect( res.values ).to eq( [num_params.times.map(&:to_s)] )
|
1364
|
+
end
|
1365
|
+
rescue PG::ProgramLimitExceeded
|
1366
|
+
# Stop silently as soon the server complains about too many params
|
1367
|
+
end
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
it "can process #copy_data input queries with row encoder" do
|
1371
|
+
@conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
1372
|
+
res2 = @conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
1373
|
+
@conn2.put_copy_data [1]
|
1374
|
+
@conn2.put_copy_data ["2"]
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
res = @conn2.exec( "SELECT * FROM copytable ORDER BY col1" )
|
1378
|
+
expect( res.values ).to eq( [["1"], ["2"]] )
|
1379
|
+
end
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
context "with default result type map" do
|
1383
|
+
before :each do
|
1384
|
+
@conn2 = described_class.new(@conninfo)
|
1385
|
+
tm = PG::TypeMapByOid.new
|
1386
|
+
tm.add_coder PG::TextDecoder::Integer.new oid: 23, format: 0
|
1387
|
+
@conn2.type_map_for_results = tm
|
1388
|
+
|
1389
|
+
row_decoder = PG::TextDecoder::CopyRow.new
|
1390
|
+
@conn2.decoder_for_get_copy_data = row_decoder
|
1391
|
+
end
|
1392
|
+
after :each do
|
1393
|
+
@conn2.close
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
it "should respect a type mapping for result" do
|
1397
|
+
res = @conn2.exec_params( "SELECT $1::INT", ["5"] )
|
1398
|
+
expect( res.values ).to eq( [[5]] )
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
it "should return the current type mapping" do
|
1402
|
+
expect( @conn2.type_map_for_results ).to be_kind_of(PG::TypeMapByOid)
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
it "should work with arbitrary number of params in conjunction with type casting" do
|
1406
|
+
begin
|
1407
|
+
3.step( 12, 0.2 ) do |exp|
|
1408
|
+
num_params = (2 ** exp).to_i
|
1409
|
+
sql = num_params.times.map{|n| "$#{n+1}::INT" }.join(",")
|
1410
|
+
params = num_params.times.to_a
|
1411
|
+
res = @conn2.exec_params( "SELECT #{sql}", params )
|
1412
|
+
expect( res.nfields ).to eq( num_params )
|
1413
|
+
expect( res.values ).to eq( [num_params.times.to_a] )
|
1414
|
+
end
|
1415
|
+
rescue PG::ProgramLimitExceeded
|
1416
|
+
# Stop silently as soon the server complains about too many params
|
1417
|
+
end
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
it "can process #copy_data output with row decoder" do
|
1421
|
+
rows = []
|
1422
|
+
res2 = @conn2.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" ) do |res|
|
1423
|
+
while row=@conn2.get_copy_data
|
1424
|
+
rows << row
|
1425
|
+
end
|
1426
|
+
end
|
1427
|
+
expect( rows ).to eq( [["1"], ["2"]] )
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
it "can type cast #copy_data output with explicit decoder" do
|
1431
|
+
tm = PG::TypeMapByColumn.new [PG::TextDecoder::Integer.new]
|
1432
|
+
row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
|
1433
|
+
rows = []
|
1434
|
+
@conn.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT", row_decoder ) do |res|
|
1435
|
+
while row=@conn.get_copy_data
|
1436
|
+
rows << row
|
1437
|
+
end
|
1438
|
+
end
|
1439
|
+
@conn.copy_data( "COPY (SELECT 3 UNION ALL SELECT 4) TO STDOUT" ) do |res|
|
1440
|
+
while row=@conn.get_copy_data( false, row_decoder )
|
1441
|
+
rows << row
|
1442
|
+
end
|
1443
|
+
end
|
1444
|
+
expect( rows ).to eq( [[1], [2], [3], [4]] )
|
1445
|
+
end
|
1446
|
+
end
|
1447
|
+
end
|
1246
1448
|
end
|