pg 0.14.1-x86-mingw32 → 0.15.0.pre.432-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +2 -0
- data/ChangeLog +424 -28
- data/Contributors.rdoc +1 -1
- data/History.rdoc +35 -0
- data/Manifest.txt +3 -0
- data/README.rdoc +5 -3
- data/Rakefile +0 -1
- data/Rakefile.cross +2 -2
- data/ext/extconf.rb +4 -0
- data/ext/gvl_wrappers.c +13 -0
- data/ext/gvl_wrappers.h +185 -0
- data/ext/pg.c +8 -4
- data/ext/pg.h +3 -0
- data/ext/pg_connection.c +451 -350
- data/ext/pg_result.c +8 -11
- data/lib/1.8/pg_ext.so +0 -0
- data/lib/1.9/pg_ext.so +0 -0
- data/lib/pg.rb +1 -1
- data/lib/pg/connection.rb +22 -2
- data/lib/pg/result.rb +6 -1
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +1 -1
- data/sample/async_copyto.rb +1 -1
- data/sample/async_mixed.rb +1 -1
- data/spec/lib/helpers.rb +16 -10
- data/spec/pg/connection_spec.rb +212 -108
- data/spec/pg/result_spec.rb +23 -8
- metadata +54 -30
- metadata.gz.sig +0 -0
data/ext/pg_result.c
CHANGED
@@ -37,7 +37,7 @@ pg_new_result(PGresult *result, VALUE rb_pgconn)
|
|
37
37
|
|
38
38
|
/*
|
39
39
|
* call-seq:
|
40
|
-
* res.
|
40
|
+
* res.check -> nil
|
41
41
|
*
|
42
42
|
* Raises appropriate exception if PG::Result is in a bad state.
|
43
43
|
*/
|
@@ -728,22 +728,20 @@ pgresult_aref(VALUE self, VALUE index)
|
|
728
728
|
return tuple;
|
729
729
|
}
|
730
730
|
|
731
|
-
|
732
731
|
/*
|
733
732
|
* call-seq:
|
734
|
-
* res.
|
733
|
+
* res.each_row { |row| ... }
|
735
734
|
*
|
736
|
-
*
|
735
|
+
* Yields each row of the result. The row is a list of column values.
|
737
736
|
*/
|
738
737
|
static VALUE
|
739
|
-
|
738
|
+
pgresult_each_row(VALUE self)
|
740
739
|
{
|
741
740
|
PGresult* result = (PGresult*) pgresult_get(self);
|
742
741
|
int row;
|
743
742
|
int field;
|
744
743
|
int num_rows = PQntuples(result);
|
745
744
|
int num_fields = PQnfields(result);
|
746
|
-
VALUE ary = rb_ary_new2(num_rows);
|
747
745
|
|
748
746
|
for ( row = 0; row < num_rows; row++ ) {
|
749
747
|
VALUE new_row = rb_ary_new2(num_fields);
|
@@ -768,11 +766,10 @@ pgresult_values(VALUE self)
|
|
768
766
|
rb_ary_store( new_row, field, val );
|
769
767
|
}
|
770
768
|
}
|
771
|
-
|
772
|
-
rb_ary_store( ary, row, new_row );
|
769
|
+
rb_yield( new_row );
|
773
770
|
}
|
774
771
|
|
775
|
-
return
|
772
|
+
return Qnil;
|
776
773
|
}
|
777
774
|
|
778
775
|
|
@@ -839,7 +836,7 @@ static VALUE
|
|
839
836
|
pgresult_field_values( VALUE self, VALUE field )
|
840
837
|
{
|
841
838
|
PGresult *result = pgresult_get( self );
|
842
|
-
const char *fieldname =
|
839
|
+
const char *fieldname = StringValuePtr( field );
|
843
840
|
int fnum = PQfnumber( result, fieldname );
|
844
841
|
|
845
842
|
if ( fnum < 0 )
|
@@ -934,7 +931,7 @@ init_pg_result()
|
|
934
931
|
rb_define_method(rb_cPGresult, "[]", pgresult_aref, 1);
|
935
932
|
rb_define_method(rb_cPGresult, "each", pgresult_each, 0);
|
936
933
|
rb_define_method(rb_cPGresult, "fields", pgresult_fields, 0);
|
937
|
-
rb_define_method(rb_cPGresult, "
|
934
|
+
rb_define_method(rb_cPGresult, "each_row", pgresult_each_row, 0);
|
938
935
|
rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
|
939
936
|
rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
|
940
937
|
}
|
data/lib/1.8/pg_ext.so
CHANGED
Binary file
|
data/lib/1.9/pg_ext.so
CHANGED
Binary file
|
data/lib/pg.rb
CHANGED
data/lib/pg/connection.rb
CHANGED
@@ -23,11 +23,19 @@ class PG::Connection
|
|
23
23
|
# This will be swapped soon for code that makes options like those required for
|
24
24
|
# PQconnectdbParams()/PQconnectStartParams(). For now, stick to an options string for
|
25
25
|
# PQconnectdb()/PQconnectStart().
|
26
|
-
|
26
|
+
|
27
|
+
# Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
|
28
|
+
# together with PQescapeLiteral().
|
29
|
+
if PG::Connection.instance_methods.find{|m| m.to_sym == :escape_literal }
|
30
|
+
appname = PG::Connection.quote_connstr( $0 )
|
31
|
+
connopts = ["fallback_application_name=#{appname}"]
|
32
|
+
else
|
33
|
+
connopts = []
|
34
|
+
end
|
27
35
|
|
28
36
|
# Handle an options hash first
|
29
37
|
if args.last.is_a?( Hash )
|
30
|
-
opthash = args.pop
|
38
|
+
opthash = args.pop
|
31
39
|
opthash.each do |key, val|
|
32
40
|
connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val)] )
|
33
41
|
end
|
@@ -58,6 +66,18 @@ class PG::Connection
|
|
58
66
|
end
|
59
67
|
|
60
68
|
|
69
|
+
### Fetch a memoized IO object created from the Connection's underlying socket.
|
70
|
+
### Using this avoids the problem of the underlying connection being closed by
|
71
|
+
### Ruby when an IO created using <tt>IO.for_fd(conn.socket)</tt> goes out of scope.
|
72
|
+
def socket_io
|
73
|
+
unless @socket_io
|
74
|
+
@socket_io = IO.for_fd( self.socket )
|
75
|
+
@socket_io.autoclose = false if @socket_io.respond_to?( :autoclose= )
|
76
|
+
end
|
77
|
+
|
78
|
+
return @socket_io
|
79
|
+
end
|
80
|
+
|
61
81
|
end # class PG::Connection
|
62
82
|
|
63
83
|
# Backward-compatible alias
|
data/lib/pg/result.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pg'
|
4
|
+
|
5
|
+
c = PG.connect( dbname: 'test' )
|
6
|
+
|
7
|
+
# this one works:
|
8
|
+
c.exec( "DROP TABLE IF EXISTS foo" )
|
9
|
+
c.exec( "CREATE TABLE foo (strings character varying[]);" )
|
10
|
+
|
11
|
+
# But using a prepared statement works:
|
12
|
+
c.set_error_verbosity( PG::PQERRORS_VERBOSE )
|
13
|
+
c.prepare( 'stmt', "INSERT INTO foo VALUES ($1);" )
|
14
|
+
|
15
|
+
# This won't work
|
16
|
+
#c.exec_prepared( 'stmt', ["ARRAY['this','that']"] )
|
17
|
+
|
18
|
+
# but this will:
|
19
|
+
c.exec_prepared( 'stmt', ["{'this','that'}"] )
|
20
|
+
|
data/sample/async_api.rb
CHANGED
@@ -29,7 +29,7 @@ abort "Connection failed: %s" % [ conn.error_message ] if
|
|
29
29
|
|
30
30
|
# Now grab a reference to the underlying socket so we know when the
|
31
31
|
# connection is established
|
32
|
-
socket =
|
32
|
+
socket = conn.socket_io
|
33
33
|
|
34
34
|
# Track the progress of the connection, waiting for the socket to become readable/writable
|
35
35
|
# before polling it
|
data/sample/async_copyto.rb
CHANGED
data/sample/async_mixed.rb
CHANGED
@@ -26,7 +26,7 @@ conn = PG.connect( CONN_OPTS ) or abort "Unable to create a new connection!"
|
|
26
26
|
abort "Connect failed: %s" % [ conn.error_message ] unless conn.status == PG::CONNECTION_OK
|
27
27
|
|
28
28
|
# Now grab a reference to the underlying socket to select() on while the query is running
|
29
|
-
socket =
|
29
|
+
socket = conn.socket_io
|
30
30
|
|
31
31
|
# Send the (asynchronous) query
|
32
32
|
output_progress "Sending query"
|
data/spec/lib/helpers.rb
CHANGED
@@ -48,7 +48,7 @@ module PG::TestingHelpers
|
|
48
48
|
attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
|
49
49
|
|
50
50
|
# $stderr.puts " attr is: %p" % [attributes]
|
51
|
-
if attributes.empty?
|
51
|
+
if attributes.empty?
|
52
52
|
return ''
|
53
53
|
else
|
54
54
|
return "\e[%sm" % attributes
|
@@ -56,7 +56,7 @@ module PG::TestingHelpers
|
|
56
56
|
end
|
57
57
|
|
58
58
|
|
59
|
-
### Colorize the given +string+ with the specified +attributes+ and return it, handling
|
59
|
+
### Colorize the given +string+ with the specified +attributes+ and return it, handling
|
60
60
|
### line-endings, color reset, etc.
|
61
61
|
def colorize( *args )
|
62
62
|
string = ''
|
@@ -110,9 +110,7 @@ module PG::TestingHelpers
|
|
110
110
|
end
|
111
111
|
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
### Run the specified command +cmd+ after redirecting stdout and stderr to the specified
|
113
|
+
### Run the specified command +cmd+ after redirecting stdout and stderr to the specified
|
116
114
|
### +logpath+, failing if the execution fails.
|
117
115
|
def log_and_run( logpath, *cmd )
|
118
116
|
cmd.flatten!
|
@@ -126,11 +124,14 @@ module PG::TestingHelpers
|
|
126
124
|
# Eliminate the noise of creating/tearing down the database by
|
127
125
|
# redirecting STDERR/STDOUT to a logfile if the Ruby interpreter
|
128
126
|
# supports fork()
|
129
|
-
|
127
|
+
logfh = File.open( logpath, File::WRONLY|File::CREAT|File::APPEND )
|
128
|
+
begin
|
129
|
+
pid = fork
|
130
|
+
rescue NotImplementedError
|
131
|
+
logfh.close
|
130
132
|
system( *cmd )
|
131
133
|
else
|
132
|
-
|
133
|
-
if pid = fork
|
134
|
+
if pid
|
134
135
|
logfh.close
|
135
136
|
else
|
136
137
|
$stdout.reopen( logfh )
|
@@ -240,10 +241,15 @@ RSpec.configure do |config|
|
|
240
241
|
|
241
242
|
config.mock_with :rspec
|
242
243
|
config.filter_run_excluding :ruby_19 if ruby_version_vec <= [1,9,1].pack( "N*" )
|
244
|
+
config.filter_run_excluding :unix if RUBY_PLATFORM =~ /mingw|mswin/
|
243
245
|
|
244
246
|
config.filter_run_excluding :postgresql_90 unless
|
245
247
|
PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
|
246
|
-
|
247
|
-
|
248
|
+
|
249
|
+
if !PG.respond_to?( :library_version )
|
250
|
+
config.filter_run_excluding( :postgresql_91, :postgresql_92 )
|
251
|
+
elsif PG.library_version < 90200
|
252
|
+
config.filter_run_excluding( :postgresql_92 )
|
253
|
+
end
|
248
254
|
end
|
249
255
|
|
data/spec/pg/connection_spec.rb
CHANGED
@@ -41,7 +41,7 @@ describe PG::Connection do
|
|
41
41
|
#
|
42
42
|
|
43
43
|
it "can create a connection option string from a Hash of options" do
|
44
|
-
optstring = described_class.parse_connect_args(
|
44
|
+
optstring = described_class.parse_connect_args(
|
45
45
|
:host => 'pgsql.example.com',
|
46
46
|
:dbname => 'db01',
|
47
47
|
'sslmode' => 'require'
|
@@ -54,14 +54,14 @@ describe PG::Connection do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
it "can create a connection option string from positional parameters" do
|
57
|
-
optstring = described_class.parse_connect_args( 'pgsql.example.com', nil, '-c geqo=off', nil,
|
57
|
+
optstring = described_class.parse_connect_args( 'pgsql.example.com', nil, '-c geqo=off', nil,
|
58
58
|
'sales' )
|
59
59
|
|
60
60
|
optstring.should be_a( String )
|
61
61
|
optstring.should =~ /(^|\s)host='pgsql.example.com'/
|
62
62
|
optstring.should =~ /(^|\s)dbname='sales'/
|
63
63
|
optstring.should =~ /(^|\s)options='-c geqo=off'/
|
64
|
-
|
64
|
+
|
65
65
|
optstring.should_not =~ /port=/
|
66
66
|
optstring.should_not =~ /tty=/
|
67
67
|
end
|
@@ -77,7 +77,8 @@ describe PG::Connection do
|
|
77
77
|
end
|
78
78
|
|
79
79
|
it "escapes single quotes and backslashes in connection parameters" do
|
80
|
-
described_class.parse_connect_args( "DB 'browser' \\" ).
|
80
|
+
described_class.parse_connect_args( "DB 'browser' \\" ).
|
81
|
+
should =~ /host='DB \\'browser\\' \\\\'/
|
81
82
|
|
82
83
|
end
|
83
84
|
|
@@ -106,7 +107,7 @@ describe PG::Connection do
|
|
106
107
|
tmpconn.finish
|
107
108
|
end
|
108
109
|
|
109
|
-
it "connects using a hash of optional connection parameters" do
|
110
|
+
it "connects using a hash of optional connection parameters", :postgresql_90 do
|
110
111
|
tmpconn = described_class.connect(
|
111
112
|
:host => 'localhost',
|
112
113
|
:port => @port,
|
@@ -122,10 +123,10 @@ describe PG::Connection do
|
|
122
123
|
}.to raise_error( ArgumentError, /extra positional parameter/i )
|
123
124
|
end
|
124
125
|
|
125
|
-
it "can connect asynchronously" do
|
126
|
+
it "can connect asynchronously", :unix do
|
126
127
|
tmpconn = described_class.connect_start( @conninfo )
|
127
128
|
tmpconn.should be_a( described_class )
|
128
|
-
socket =
|
129
|
+
socket = tmpconn.socket_io
|
129
130
|
status = tmpconn.connect_poll
|
130
131
|
|
131
132
|
while status != PG::PGRES_POLLING_OK
|
@@ -144,13 +145,13 @@ describe PG::Connection do
|
|
144
145
|
tmpconn.finish
|
145
146
|
end
|
146
147
|
|
147
|
-
it "can connect asynchronously for the duration of a block" do
|
148
|
+
it "can connect asynchronously for the duration of a block", :unix do
|
148
149
|
conn = nil
|
149
150
|
|
150
151
|
described_class.connect_start(@conninfo) do |tmpconn|
|
151
152
|
tmpconn.should be_a( described_class )
|
152
153
|
conn = tmpconn
|
153
|
-
socket =
|
154
|
+
socket = tmpconn.socket_io
|
154
155
|
status = tmpconn.connect_poll
|
155
156
|
|
156
157
|
while status != PG::PGRES_POLLING_OK
|
@@ -285,39 +286,58 @@ describe PG::Connection do
|
|
285
286
|
end
|
286
287
|
|
287
288
|
|
289
|
+
it "supports parameters passed to #exec (backward compatibility)" do
|
290
|
+
@conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
|
291
|
+
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
|
292
|
+
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
|
293
|
+
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
294
|
+
|
295
|
+
res = @conn.exec( "SELECT name FROM students WHERE age >= $1", [6] )
|
296
|
+
res.values.should == [ ['Wally'], ['Sally'] ]
|
297
|
+
end
|
298
|
+
|
299
|
+
it "supports explicitly calling #exec_params" do
|
300
|
+
@conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
|
301
|
+
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
|
302
|
+
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
|
303
|
+
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
304
|
+
|
305
|
+
res = @conn.exec_params( "SELECT name FROM students WHERE age >= $1", [6] )
|
306
|
+
res.values.should == [ ['Wally'], ['Sally'] ]
|
307
|
+
end
|
308
|
+
|
309
|
+
|
288
310
|
it "can wait for NOTIFY events" do
|
289
311
|
@conn.exec( 'ROLLBACK' )
|
290
312
|
@conn.exec( 'LISTEN woo' )
|
291
313
|
|
292
|
-
|
314
|
+
t = Thread.new do
|
293
315
|
begin
|
294
316
|
conn = described_class.connect( @conninfo )
|
295
317
|
sleep 1
|
296
|
-
conn.
|
318
|
+
conn.async_exec( 'NOTIFY woo' )
|
297
319
|
ensure
|
298
320
|
conn.finish
|
299
|
-
exit!
|
300
321
|
end
|
301
322
|
end
|
302
323
|
|
303
324
|
@conn.wait_for_notify( 10 ).should == 'woo'
|
304
325
|
@conn.exec( 'UNLISTEN woo' )
|
305
326
|
|
306
|
-
|
327
|
+
t.join
|
307
328
|
end
|
308
329
|
|
309
330
|
it "calls a block for NOTIFY events if one is given" do
|
310
331
|
@conn.exec( 'ROLLBACK' )
|
311
332
|
@conn.exec( 'LISTEN woo' )
|
312
333
|
|
313
|
-
|
334
|
+
t = Thread.new do
|
314
335
|
begin
|
315
336
|
conn = described_class.connect( @conninfo )
|
316
337
|
sleep 1
|
317
|
-
conn.
|
338
|
+
conn.async_exec( 'NOTIFY woo' )
|
318
339
|
ensure
|
319
340
|
conn.finish
|
320
|
-
exit!
|
321
341
|
end
|
322
342
|
end
|
323
343
|
|
@@ -328,7 +348,7 @@ describe PG::Connection do
|
|
328
348
|
|
329
349
|
@conn.exec( 'UNLISTEN woo' )
|
330
350
|
|
331
|
-
|
351
|
+
t.join
|
332
352
|
end
|
333
353
|
|
334
354
|
it "doesn't collapse sequential notifications" do
|
@@ -337,20 +357,15 @@ describe PG::Connection do
|
|
337
357
|
@conn.exec( 'LISTEN war' )
|
338
358
|
@conn.exec( 'LISTEN woz' )
|
339
359
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
conn.finish
|
348
|
-
exit!
|
349
|
-
end
|
360
|
+
begin
|
361
|
+
conn = described_class.connect( @conninfo )
|
362
|
+
conn.exec( 'NOTIFY woo' )
|
363
|
+
conn.exec( 'NOTIFY war' )
|
364
|
+
conn.exec( 'NOTIFY woz' )
|
365
|
+
ensure
|
366
|
+
conn.finish
|
350
367
|
end
|
351
368
|
|
352
|
-
Process.wait( pid )
|
353
|
-
|
354
369
|
channels = []
|
355
370
|
3.times do
|
356
371
|
channels << @conn.wait_for_notify( 2 )
|
@@ -369,19 +384,13 @@ describe PG::Connection do
|
|
369
384
|
@conn.exec( 'ROLLBACK' )
|
370
385
|
@conn.exec( 'LISTEN woo' )
|
371
386
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
conn.finish
|
378
|
-
exit!
|
379
|
-
end
|
387
|
+
begin
|
388
|
+
conn = described_class.connect( @conninfo )
|
389
|
+
conn.exec( 'NOTIFY woo' )
|
390
|
+
ensure
|
391
|
+
conn.finish
|
380
392
|
end
|
381
393
|
|
382
|
-
# Wait for the forked child to send the notification
|
383
|
-
Process.wait( pid )
|
384
|
-
|
385
394
|
# Cause the notification to buffer, but not be read yet
|
386
395
|
@conn.exec( 'SELECT 1' )
|
387
396
|
|
@@ -424,16 +433,17 @@ describe PG::Connection do
|
|
424
433
|
|
425
434
|
|
426
435
|
it "described_class#block shouldn't block a second thread" do
|
436
|
+
start = Time.now
|
427
437
|
t = Thread.new do
|
428
438
|
@conn.send_query( "select pg_sleep(3)" )
|
429
439
|
@conn.block
|
430
440
|
end
|
431
441
|
|
432
|
-
|
442
|
+
sleep 0.5
|
433
443
|
t.should be_alive()
|
434
|
-
|
435
444
|
@conn.cancel
|
436
445
|
t.join
|
446
|
+
(Time.now - start).should < 3
|
437
447
|
end
|
438
448
|
|
439
449
|
it "described_class#block should allow a timeout" do
|
@@ -505,14 +515,14 @@ describe PG::Connection do
|
|
505
515
|
end
|
506
516
|
|
507
517
|
|
508
|
-
it "can connect asynchronously" do
|
518
|
+
it "can connect asynchronously", :unix do
|
509
519
|
serv = TCPServer.new( '127.0.0.1', 54320 )
|
510
520
|
conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
511
|
-
|
512
|
-
select( nil, [
|
521
|
+
[PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK].should include conn.connect_poll
|
522
|
+
select( nil, [conn.socket_io], nil, 0.2 )
|
513
523
|
serv.close
|
514
524
|
if conn.connect_poll == PG::PGRES_POLLING_READING
|
515
|
-
select( [
|
525
|
+
select( [conn.socket_io], nil, nil, 0.2 )
|
516
526
|
end
|
517
527
|
conn.connect_poll.should == PG::PGRES_POLLING_FAILED
|
518
528
|
end
|
@@ -541,20 +551,19 @@ describe PG::Connection do
|
|
541
551
|
pending "only works with a PostgreSQL >= 9.0 server" if @conn.server_version < 9_00_00
|
542
552
|
end
|
543
553
|
|
554
|
+
it "sets the fallback_application_name on new connections" do
|
555
|
+
PG::Connection.parse_connect_args( 'dbname=test' ).should include( $0 )
|
556
|
+
end
|
557
|
+
|
544
558
|
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
545
559
|
"any number of arguments" do
|
546
560
|
|
547
561
|
@conn.exec( 'ROLLBACK' )
|
548
562
|
@conn.exec( 'LISTEN knees' )
|
549
563
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
conn.finish
|
554
|
-
exit!
|
555
|
-
end
|
556
|
-
|
557
|
-
Process.wait( pid )
|
564
|
+
conn = described_class.connect( @conninfo )
|
565
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
566
|
+
conn.finish
|
558
567
|
|
559
568
|
event, pid, msg = nil
|
560
569
|
@conn.wait_for_notify( 10 ) do |*args|
|
@@ -571,14 +580,9 @@ describe PG::Connection do
|
|
571
580
|
@conn.exec( 'ROLLBACK' )
|
572
581
|
@conn.exec( 'LISTEN knees' )
|
573
582
|
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
conn.finish
|
578
|
-
exit!
|
579
|
-
end
|
580
|
-
|
581
|
-
Process.wait( pid )
|
583
|
+
conn = described_class.connect( @conninfo )
|
584
|
+
conn.exec( %Q{NOTIFY knees} )
|
585
|
+
conn.finish
|
582
586
|
|
583
587
|
event, pid = nil
|
584
588
|
@conn.wait_for_notify( nil ) do |*args|
|
@@ -594,14 +598,9 @@ describe PG::Connection do
|
|
594
598
|
@conn.exec( 'ROLLBACK' )
|
595
599
|
@conn.exec( 'LISTEN knees' )
|
596
600
|
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
conn.finish
|
601
|
-
exit!
|
602
|
-
end
|
603
|
-
|
604
|
-
Process.wait( pid )
|
601
|
+
conn = described_class.connect( @conninfo )
|
602
|
+
conn.exec( %Q{NOTIFY knees} )
|
603
|
+
conn.finish
|
605
604
|
|
606
605
|
payload = :notnil
|
607
606
|
@conn.wait_for_notify( nil ) do |*args|
|
@@ -618,14 +617,9 @@ describe PG::Connection do
|
|
618
617
|
@conn.exec( 'ROLLBACK' )
|
619
618
|
@conn.exec( 'LISTEN knees' )
|
620
619
|
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
conn.finish
|
625
|
-
exit!
|
626
|
-
end
|
627
|
-
|
628
|
-
Process.wait( pid )
|
620
|
+
conn = described_class.connect( @conninfo )
|
621
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
622
|
+
conn.finish
|
629
623
|
|
630
624
|
event, pid, msg = nil
|
631
625
|
@conn.wait_for_notify( 10 ) do |arg1, arg2|
|
@@ -644,14 +638,9 @@ describe PG::Connection do
|
|
644
638
|
@conn.exec( 'ROLLBACK' )
|
645
639
|
@conn.exec( 'LISTEN knees' )
|
646
640
|
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
conn.finish
|
651
|
-
exit!
|
652
|
-
end
|
653
|
-
|
654
|
-
Process.wait( pid )
|
641
|
+
conn = described_class.connect( @conninfo )
|
642
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
643
|
+
conn.finish
|
655
644
|
|
656
645
|
notification_received = false
|
657
646
|
@conn.wait_for_notify( 10 ) do
|
@@ -668,14 +657,9 @@ describe PG::Connection do
|
|
668
657
|
@conn.exec( 'ROLLBACK' )
|
669
658
|
@conn.exec( 'LISTEN knees' )
|
670
659
|
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
conn.finish
|
675
|
-
exit!
|
676
|
-
end
|
677
|
-
|
678
|
-
Process.wait( pid )
|
660
|
+
conn = described_class.connect( @conninfo )
|
661
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
662
|
+
conn.finish
|
679
663
|
|
680
664
|
event, pid, msg = nil
|
681
665
|
@conn.wait_for_notify( 10 ) do |arg1, arg2, arg3|
|
@@ -726,22 +710,71 @@ describe PG::Connection do
|
|
726
710
|
|
727
711
|
end
|
728
712
|
|
729
|
-
context "
|
713
|
+
context "under PostgreSQL 9.2 client library", :postgresql_92 do
|
714
|
+
describe "set_single_row_mode" do
|
715
|
+
|
716
|
+
it "raises an error when called at the wrong time" do
|
717
|
+
expect {
|
718
|
+
@conn.set_single_row_mode
|
719
|
+
}.to raise_error(PG::Error)
|
720
|
+
end
|
721
|
+
|
722
|
+
it "should work in single row mode" do
|
723
|
+
@conn.send_query( "SELECT generate_series(1,10)" )
|
724
|
+
@conn.set_single_row_mode
|
725
|
+
|
726
|
+
results = []
|
727
|
+
loop do
|
728
|
+
@conn.block
|
729
|
+
res = @conn.get_result or break
|
730
|
+
results << res
|
731
|
+
end
|
732
|
+
results.length.should == 11
|
733
|
+
results[0..-2].each do |res|
|
734
|
+
res.result_status.should == PG::PGRES_SINGLE_TUPLE
|
735
|
+
values = res.field_values('generate_series')
|
736
|
+
values.length.should == 1
|
737
|
+
values.first.to_i.should > 0
|
738
|
+
end
|
739
|
+
results.last.result_status.should == PG::PGRES_TUPLES_OK
|
740
|
+
results.last.ntuples.should == 0
|
741
|
+
end
|
730
742
|
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
escaped_bytes = described_class.escape_bytea( in_bytes )
|
735
|
-
out_bytes = nil
|
743
|
+
it "should receive rows before entire query is finished" do
|
744
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, pg_sleep(1);" )
|
745
|
+
@conn.set_single_row_mode
|
736
746
|
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
747
|
+
start_time = Time.now
|
748
|
+
first_row_time = nil
|
749
|
+
loop do
|
750
|
+
res = @conn.get_result or break
|
751
|
+
res.check
|
752
|
+
first_row_time = Time.now unless first_row_time
|
753
|
+
end
|
754
|
+
(Time.now - start_time).should >= 1.0
|
755
|
+
(first_row_time - start_time).should < 1.0
|
741
756
|
end
|
742
757
|
|
743
|
-
|
758
|
+
it "should receive rows before entire query fails" do
|
759
|
+
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
760
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
|
761
|
+
@conn.set_single_row_mode
|
762
|
+
|
763
|
+
first_result = nil
|
764
|
+
expect do
|
765
|
+
loop do
|
766
|
+
res = @conn.get_result or break
|
767
|
+
res.check
|
768
|
+
first_result ||= res
|
769
|
+
end
|
770
|
+
end.to raise_error(PG::Error)
|
771
|
+
first_result.kind_of?(PG::Result).should be_true
|
772
|
+
first_result.result_status.should == PG::PGRES_SINGLE_TUPLE
|
773
|
+
end
|
744
774
|
end
|
775
|
+
end
|
776
|
+
|
777
|
+
context "multinationalization support", :ruby_19 do
|
745
778
|
|
746
779
|
describe "rubyforge #22925: m17n support" do
|
747
780
|
it "should return results in the same encoding as the client (iso-8859-1)" do
|
@@ -818,7 +851,7 @@ describe PG::Connection do
|
|
818
851
|
escaped.encoding.should == Encoding::EUC_JP
|
819
852
|
end
|
820
853
|
|
821
|
-
it "escapes string as literal" do
|
854
|
+
it "escapes string as literal", :postgresql_90 do
|
822
855
|
original = "string to\0 escape"
|
823
856
|
escaped = @conn.escape_literal( original )
|
824
857
|
escaped.should == "'string to'"
|
@@ -852,11 +885,11 @@ describe PG::Connection do
|
|
852
885
|
it "allows users of the async interface to set the client_encoding to the default_internal" do
|
853
886
|
begin
|
854
887
|
prev_encoding = Encoding.default_internal
|
855
|
-
Encoding.default_internal = Encoding::
|
888
|
+
Encoding.default_internal = Encoding::KOI8_R
|
856
889
|
|
857
890
|
@conn.set_default_encoding
|
858
891
|
|
859
|
-
@conn.internal_encoding.should == Encoding::
|
892
|
+
@conn.internal_encoding.should == Encoding::KOI8_R
|
860
893
|
ensure
|
861
894
|
Encoding.default_internal = prev_encoding
|
862
895
|
end
|
@@ -886,5 +919,76 @@ describe PG::Connection do
|
|
886
919
|
conn.finish if conn
|
887
920
|
end
|
888
921
|
|
922
|
+
it "receives properly encoded messages in the notice callbacks", :postgresql_90 do
|
923
|
+
[:receiver, :processor].each do |kind|
|
924
|
+
notices = []
|
925
|
+
@conn.internal_encoding = 'utf-8'
|
926
|
+
if kind == :processor
|
927
|
+
@conn.set_notice_processor do |msg|
|
928
|
+
notices << msg
|
929
|
+
end
|
930
|
+
else
|
931
|
+
@conn.set_notice_receiver do |result|
|
932
|
+
notices << result.error_message
|
933
|
+
end
|
934
|
+
end
|
935
|
+
|
936
|
+
3.times do
|
937
|
+
@conn.exec "do $$ BEGIN RAISE NOTICE '世界線航跡蔵'; END; $$ LANGUAGE plpgsql;"
|
938
|
+
end
|
939
|
+
|
940
|
+
notices.length.should == 3
|
941
|
+
notices.each do |notice|
|
942
|
+
notice.should =~ /^NOTICE:.*世界線航跡蔵/
|
943
|
+
notice.encoding.should == Encoding::UTF_8
|
944
|
+
end
|
945
|
+
@conn.set_notice_receiver
|
946
|
+
@conn.set_notice_processor
|
947
|
+
end
|
948
|
+
end
|
949
|
+
|
950
|
+
it "receives properly encoded text from wait_for_notify", :postgresql_90 do
|
951
|
+
@conn.internal_encoding = 'utf-8'
|
952
|
+
@conn.exec( 'ROLLBACK' )
|
953
|
+
@conn.exec( 'LISTEN "Möhre"' )
|
954
|
+
@conn.exec( %Q{NOTIFY "Möhre", '世界線航跡蔵'} )
|
955
|
+
event, pid, msg = nil
|
956
|
+
@conn.wait_for_notify( 10 ) do |*args|
|
957
|
+
event, pid, msg = *args
|
958
|
+
end
|
959
|
+
@conn.exec( 'UNLISTEN "Möhre"' )
|
960
|
+
|
961
|
+
event.should == "Möhre"
|
962
|
+
event.encoding.should == Encoding::UTF_8
|
963
|
+
msg.should == '世界線航跡蔵'
|
964
|
+
msg.encoding.should == Encoding::UTF_8
|
965
|
+
end
|
966
|
+
|
967
|
+
it "returns properly encoded text from notifies", :postgresql_90 do
|
968
|
+
@conn.internal_encoding = 'utf-8'
|
969
|
+
@conn.exec( 'ROLLBACK' )
|
970
|
+
@conn.exec( 'LISTEN "Möhre"' )
|
971
|
+
@conn.exec( %Q{NOTIFY "Möhre", '世界線航跡蔵'} )
|
972
|
+
@conn.exec( 'UNLISTEN "Möhre"' )
|
973
|
+
|
974
|
+
notification = @conn.notifies
|
975
|
+
notification[:relname].should == "Möhre"
|
976
|
+
notification[:relname].encoding.should == Encoding::UTF_8
|
977
|
+
notification[:extra].should == '世界線航跡蔵'
|
978
|
+
notification[:extra].encoding.should == Encoding::UTF_8
|
979
|
+
notification[:be_pid].should > 0
|
980
|
+
end
|
981
|
+
end
|
982
|
+
|
983
|
+
context "OS thread support", :ruby_19 do
|
984
|
+
it "described_class#exec shouldn't block a second thread" do
|
985
|
+
t = Thread.new do
|
986
|
+
@conn.exec( "select pg_sleep(1)" )
|
987
|
+
end
|
988
|
+
|
989
|
+
sleep 0.5
|
990
|
+
t.should be_alive()
|
991
|
+
t.join
|
992
|
+
end
|
889
993
|
end
|
890
994
|
end
|