pg 0.14.1-x86-mingw32 → 0.15.0.pre.432-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.
- 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
|