pg-ct 0.10.0
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/BSD +23 -0
- data/ChangeLog +585 -0
- data/Contributors +30 -0
- data/GPL +340 -0
- data/LICENSE +58 -0
- data/README +68 -0
- data/README.OS_X +19 -0
- data/README.ja +183 -0
- data/README.windows +72 -0
- data/Rakefile +359 -0
- data/Rakefile.local +298 -0
- data/ext/compat.c +541 -0
- data/ext/compat.h +180 -0
- data/ext/extconf.rb +147 -0
- data/ext/pg.c +4307 -0
- data/ext/pg.h +49 -0
- data/lib/pg.rb +14 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/documentation.rb +123 -0
- data/rake/helpers.rb +502 -0
- data/rake/hg.rb +287 -0
- data/rake/manual.rb +787 -0
- data/rake/packaging.rb +129 -0
- data/rake/publishing.rb +341 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +151 -0
- data/rake/verifytask.rb +64 -0
- data/spec/data/expected_trace.out +26 -0
- data/spec/data/random_binary_data +0 -0
- data/spec/lib/helpers.rb +236 -0
- data/spec/m17n_spec.rb +151 -0
- data/spec/pgconn_spec.rb +437 -0
- data/spec/pgresult_spec.rb +215 -0
- metadata +126 -0
data/spec/m17n_spec.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
#!/usr/bin/env spec
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
BEGIN {
|
5
|
+
require 'pathname'
|
6
|
+
require 'rbconfig'
|
7
|
+
|
8
|
+
basedir = Pathname( __FILE__ ).dirname.parent
|
9
|
+
libdir = basedir + 'lib'
|
10
|
+
archlib = libdir + Config::CONFIG['sitearch']
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
|
13
|
+
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
14
|
+
$LOAD_PATH.unshift( archlib.to_s ) unless $LOAD_PATH.include?( archlib.to_s )
|
15
|
+
}
|
16
|
+
|
17
|
+
require 'rspec'
|
18
|
+
require 'spec/lib/helpers'
|
19
|
+
require 'pg'
|
20
|
+
|
21
|
+
describe "multinationalization support", :ruby_19 => true do
|
22
|
+
include PgTestingHelpers
|
23
|
+
|
24
|
+
before( :all ) do
|
25
|
+
@conn = setup_testing_db( "m17n" )
|
26
|
+
@conn.exec( 'BEGIN' )
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
it "should return the same bytes in text format that are sent as inline text" do
|
31
|
+
binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
|
32
|
+
in_bytes = File.open(binary_file, 'r:ASCII-8BIT').read
|
33
|
+
|
34
|
+
out_bytes = nil
|
35
|
+
@conn.transaction do |conn|
|
36
|
+
conn.exec("SET standard_conforming_strings=on")
|
37
|
+
res = conn.exec("VALUES ('#{PGconn.escape_bytea(in_bytes)}'::bytea)", [], 0)
|
38
|
+
out_bytes = PGconn.unescape_bytea(res[0]['column1'])
|
39
|
+
end
|
40
|
+
out_bytes.should == in_bytes
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "rubyforge #22925: m17n support" do
|
44
|
+
it "should return results in the same encoding as the client (iso-8859-1)" do
|
45
|
+
out_string = nil
|
46
|
+
@conn.transaction do |conn|
|
47
|
+
conn.internal_encoding = 'iso8859-1'
|
48
|
+
res = conn.exec("VALUES ('fantasia')", [], 0)
|
49
|
+
out_string = res[0]['column1']
|
50
|
+
end
|
51
|
+
out_string.should == 'fantasia'
|
52
|
+
out_string.encoding.should == Encoding::ISO8859_1
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should return results in the same encoding as the client (utf-8)" do
|
56
|
+
out_string = nil
|
57
|
+
@conn.transaction do |conn|
|
58
|
+
conn.internal_encoding = 'utf-8'
|
59
|
+
res = conn.exec("VALUES ('世界線航跡蔵')", [], 0)
|
60
|
+
out_string = res[0]['column1']
|
61
|
+
end
|
62
|
+
out_string.should == '世界線航跡蔵'
|
63
|
+
out_string.encoding.should == Encoding::UTF_8
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should return results in the same encoding as the client (EUC-JP)" do
|
67
|
+
out_string = nil
|
68
|
+
@conn.transaction do |conn|
|
69
|
+
conn.internal_encoding = 'EUC-JP'
|
70
|
+
stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
|
71
|
+
res = conn.exec(stmt, [], 0)
|
72
|
+
out_string = res[0]['column1']
|
73
|
+
end
|
74
|
+
out_string.should == '世界線航跡蔵'.encode('EUC-JP')
|
75
|
+
out_string.encoding.should == Encoding::EUC_JP
|
76
|
+
end
|
77
|
+
|
78
|
+
it "returns the results in the correct encoding even if the client_encoding has " +
|
79
|
+
"changed since the results were fetched" do
|
80
|
+
out_string = nil
|
81
|
+
@conn.transaction do |conn|
|
82
|
+
conn.internal_encoding = 'EUC-JP'
|
83
|
+
stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
|
84
|
+
res = conn.exec(stmt, [], 0)
|
85
|
+
conn.internal_encoding = 'utf-8'
|
86
|
+
out_string = res[0]['column1']
|
87
|
+
end
|
88
|
+
out_string.should == '世界線航跡蔵'.encode('EUC-JP')
|
89
|
+
out_string.encoding.should == Encoding::EUC_JP
|
90
|
+
end
|
91
|
+
|
92
|
+
it "the connection should return ASCII-8BIT when the server encoding is SQL_ASCII" do
|
93
|
+
@conn.external_encoding.should == Encoding::ASCII_8BIT
|
94
|
+
end
|
95
|
+
|
96
|
+
it "works around the unsupported JOHAB encoding by returning stuff in 'ASCII_8BIT'" do
|
97
|
+
pending "figuring out how to create a string in the JOHAB encoding" do
|
98
|
+
out_string = nil
|
99
|
+
@conn.transaction do |conn|
|
100
|
+
conn.exec( "set client_encoding = 'JOHAB';" )
|
101
|
+
stmt = "VALUES ('foo')".encode('JOHAB')
|
102
|
+
res = conn.exec( stmt, [], 0 )
|
103
|
+
out_string = res[0]['column1']
|
104
|
+
end
|
105
|
+
out_string.should == 'foo'.encode( Encoding::ASCII_8BIT )
|
106
|
+
out_string.encoding.should == Encoding::ASCII_8BIT
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it "uses the client encoding for escaped string" do
|
111
|
+
original = "string to escape".force_encoding( "euc-jp" )
|
112
|
+
@conn.set_client_encoding( "euc_jp" )
|
113
|
+
escaped = @conn.escape( original )
|
114
|
+
escaped.encoding.should == Encoding::EUC_JP
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "Ruby 1.9.x default_internal encoding" do
|
119
|
+
|
120
|
+
it "honors the Encoding.default_internal if it's set and the synchronous interface is used" do
|
121
|
+
@conn.transaction do |txn_conn|
|
122
|
+
txn_conn.internal_encoding = Encoding::ISO8859_1
|
123
|
+
txn_conn.exec( "CREATE TABLE defaultinternaltest ( foo text )" )
|
124
|
+
txn_conn.exec( "INSERT INTO defaultinternaltest VALUES ('Grün und Weiß')" )
|
125
|
+
end
|
126
|
+
|
127
|
+
begin
|
128
|
+
prev_encoding = Encoding.default_internal
|
129
|
+
Encoding.default_internal = Encoding::UTF_8
|
130
|
+
|
131
|
+
conn = PGconn.connect( @conninfo )
|
132
|
+
conn.internal_encoding.should == Encoding::UTF_8
|
133
|
+
res = conn.exec( "SELECT foo FROM defaultinternaltest" )
|
134
|
+
res[0]['foo'].encoding.should == Encoding::UTF_8
|
135
|
+
ensure
|
136
|
+
conn.finish if conn
|
137
|
+
Encoding.default_internal = prev_encoding
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
after( :each ) do
|
145
|
+
@conn.exec( 'ROLLBACK' ) if @conn
|
146
|
+
end
|
147
|
+
|
148
|
+
after( :all ) do
|
149
|
+
teardown_testing_db( @conn ) if @conn
|
150
|
+
end
|
151
|
+
end
|
data/spec/pgconn_spec.rb
ADDED
@@ -0,0 +1,437 @@
|
|
1
|
+
#!/usr/bin/env spec
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
BEGIN {
|
5
|
+
require 'pathname'
|
6
|
+
require 'rbconfig'
|
7
|
+
|
8
|
+
basedir = Pathname( __FILE__ ).dirname.parent
|
9
|
+
libdir = basedir + 'lib'
|
10
|
+
archlib = libdir + Config::CONFIG['sitearch']
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
|
13
|
+
$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
|
14
|
+
$LOAD_PATH.unshift( archlib.to_s ) unless $LOAD_PATH.include?( archlib.to_s )
|
15
|
+
}
|
16
|
+
|
17
|
+
require 'rspec'
|
18
|
+
require 'spec/lib/helpers'
|
19
|
+
require 'pg'
|
20
|
+
require 'timeout'
|
21
|
+
|
22
|
+
describe PGconn do
|
23
|
+
include PgTestingHelpers
|
24
|
+
|
25
|
+
before( :all ) do
|
26
|
+
@conn = setup_testing_db( "PGconn" )
|
27
|
+
end
|
28
|
+
|
29
|
+
before( :each ) do
|
30
|
+
@conn.exec( 'BEGIN' )
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
it "should connect successfully with connection string" do
|
35
|
+
tmpconn = PGconn.connect(@conninfo)
|
36
|
+
tmpconn.status.should== PGconn::CONNECTION_OK
|
37
|
+
tmpconn.finish
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should connect using 7 arguments converted to strings" do
|
41
|
+
tmpconn = PGconn.connect('localhost', @port, nil, nil, :test, nil, nil)
|
42
|
+
tmpconn.status.should== PGconn::CONNECTION_OK
|
43
|
+
tmpconn.finish
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should connect using hash" do
|
47
|
+
tmpconn = PGconn.connect(
|
48
|
+
:host => 'localhost',
|
49
|
+
:port => @port,
|
50
|
+
:dbname => :test)
|
51
|
+
tmpconn.status.should== PGconn::CONNECTION_OK
|
52
|
+
tmpconn.finish
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should connect asynchronously" do
|
56
|
+
tmpconn = PGconn.connect_start(@conninfo)
|
57
|
+
socket = IO.for_fd(tmpconn.socket)
|
58
|
+
status = tmpconn.connect_poll
|
59
|
+
while(status != PGconn::PGRES_POLLING_OK) do
|
60
|
+
if(status == PGconn::PGRES_POLLING_READING)
|
61
|
+
if(not select([socket],[],[],5.0))
|
62
|
+
raise "Asynchronous connection timed out!"
|
63
|
+
end
|
64
|
+
elsif(status == PGconn::PGRES_POLLING_WRITING)
|
65
|
+
if(not select([],[socket],[],5.0))
|
66
|
+
raise "Asynchronous connection timed out!"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
status = tmpconn.connect_poll
|
70
|
+
end
|
71
|
+
tmpconn.status.should== PGconn::CONNECTION_OK
|
72
|
+
tmpconn.finish
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should not leave stale server connections after finish" do
|
76
|
+
PGconn.connect(@conninfo).finish
|
77
|
+
sleep 0.5
|
78
|
+
res = @conn.exec(%[SELECT COUNT(*) AS n FROM pg_stat_activity
|
79
|
+
WHERE usename IS NOT NULL])
|
80
|
+
# there's still the global @conn, but should be no more
|
81
|
+
res[0]['n'].should == '1'
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
EXPECTED_TRACE_OUTPUT = %{
|
86
|
+
To backend> Msg Q
|
87
|
+
To backend> "SELECT 1 AS one"
|
88
|
+
To backend> Msg complete, length 21
|
89
|
+
From backend> T
|
90
|
+
From backend (#4)> 28
|
91
|
+
From backend (#2)> 1
|
92
|
+
From backend> "one"
|
93
|
+
From backend (#4)> 0
|
94
|
+
From backend (#2)> 0
|
95
|
+
From backend (#4)> 23
|
96
|
+
From backend (#2)> 4
|
97
|
+
From backend (#4)> -1
|
98
|
+
From backend (#2)> 0
|
99
|
+
From backend> D
|
100
|
+
From backend (#4)> 11
|
101
|
+
From backend (#2)> 1
|
102
|
+
From backend (#4)> 1
|
103
|
+
From backend (1)> 1
|
104
|
+
From backend> C
|
105
|
+
From backend (#4)> 13
|
106
|
+
From backend> "SELECT 1"
|
107
|
+
From backend> Z
|
108
|
+
From backend (#4)> 5
|
109
|
+
From backend> Z
|
110
|
+
From backend (#4)> 5
|
111
|
+
From backend> T
|
112
|
+
}.gsub( /^\t{2}/, '' ).lstrip
|
113
|
+
|
114
|
+
unless RUBY_PLATFORM =~ /mswin|mingw/
|
115
|
+
it "should trace and untrace client-server communication" do
|
116
|
+
# be careful to explicitly close files so that the
|
117
|
+
# directory can be removed and we don't have to wait for
|
118
|
+
# the GC to run.
|
119
|
+
trace_file = @test_directory + "test_trace.out"
|
120
|
+
trace_io = trace_file.open( 'w', 0600 )
|
121
|
+
@conn.trace( trace_io )
|
122
|
+
trace_io.close
|
123
|
+
|
124
|
+
res = @conn.exec("SELECT 1 AS one")
|
125
|
+
@conn.untrace
|
126
|
+
|
127
|
+
res = @conn.exec("SELECT 2 AS two")
|
128
|
+
|
129
|
+
trace_data = trace_file.read
|
130
|
+
|
131
|
+
expected_trace_output = EXPECTED_TRACE_OUTPUT.dup
|
132
|
+
# For PostgreSQL < 9.0, the output will be different:
|
133
|
+
# -From backend (#4)> 13
|
134
|
+
# -From backend> "SELECT 1"
|
135
|
+
# +From backend (#4)> 11
|
136
|
+
# +From backend> "SELECT"
|
137
|
+
if @conn.server_version < 90000
|
138
|
+
expected_trace_output.sub!( /From backend \(#4\)> 13/, 'From backend (#4)> 11' )
|
139
|
+
expected_trace_output.sub!( /From backend> "SELECT 1"/, 'From backend> "SELECT"' )
|
140
|
+
end
|
141
|
+
|
142
|
+
trace_data.should == expected_trace_output
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it "allows a query to be cancelled" do
|
147
|
+
error = false
|
148
|
+
@conn.send_query("SELECT pg_sleep(1000)")
|
149
|
+
@conn.cancel
|
150
|
+
tmpres = @conn.get_result
|
151
|
+
if(tmpres.result_status != PGresult::PGRES_TUPLES_OK)
|
152
|
+
error = true
|
153
|
+
end
|
154
|
+
error.should == true
|
155
|
+
end
|
156
|
+
|
157
|
+
it "automatically rolls back a transaction started with PGconn#transaction if an exception " +
|
158
|
+
"is raised" do
|
159
|
+
# abort the per-example transaction so we can test our own
|
160
|
+
@conn.exec( 'ROLLBACK' )
|
161
|
+
|
162
|
+
res = nil
|
163
|
+
@conn.exec( "CREATE TABLE pie ( flavor TEXT )" )
|
164
|
+
|
165
|
+
expect {
|
166
|
+
res = @conn.transaction do
|
167
|
+
@conn.exec( "INSERT INTO pie VALUES ('rhubarb'), ('cherry'), ('schizophrenia')" )
|
168
|
+
raise "Oh noes! All pie is gone!"
|
169
|
+
end
|
170
|
+
}.to raise_exception( RuntimeError, /all pie is gone/i )
|
171
|
+
|
172
|
+
res = @conn.exec( "SELECT * FROM pie" )
|
173
|
+
res.ntuples.should == 0
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should not read past the end of a large object" do
|
177
|
+
@conn.transaction do
|
178
|
+
oid = @conn.lo_create( 0 )
|
179
|
+
fd = @conn.lo_open( oid, PGconn::INV_READ|PGconn::INV_WRITE )
|
180
|
+
@conn.lo_write( fd, "foobar" )
|
181
|
+
@conn.lo_read( fd, 10 ).should be_nil()
|
182
|
+
@conn.lo_lseek( fd, 0, PGconn::SEEK_SET )
|
183
|
+
@conn.lo_read( fd, 10 ).should == 'foobar'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
it "can wait for NOTIFY events" do
|
189
|
+
@conn.exec( 'ROLLBACK' )
|
190
|
+
@conn.exec( 'LISTEN woo' )
|
191
|
+
|
192
|
+
pid = fork do
|
193
|
+
begin
|
194
|
+
conn = PGconn.connect( @conninfo )
|
195
|
+
sleep 1
|
196
|
+
conn.exec( 'NOTIFY woo' )
|
197
|
+
ensure
|
198
|
+
conn.finish
|
199
|
+
exit!
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
@conn.wait_for_notify( 10 ).should == 'woo'
|
204
|
+
@conn.exec( 'UNLISTEN woo' )
|
205
|
+
|
206
|
+
Process.wait( pid )
|
207
|
+
end
|
208
|
+
|
209
|
+
it "calls a block for NOTIFY events if one is given" do
|
210
|
+
@conn.exec( 'ROLLBACK' )
|
211
|
+
@conn.exec( 'LISTEN woo' )
|
212
|
+
|
213
|
+
pid = fork do
|
214
|
+
begin
|
215
|
+
conn = PGconn.connect( @conninfo )
|
216
|
+
sleep 1
|
217
|
+
conn.exec( 'NOTIFY woo' )
|
218
|
+
ensure
|
219
|
+
conn.finish
|
220
|
+
exit!
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
eventpid = event = nil
|
225
|
+
@conn.wait_for_notify( 10 ) {|*args| event, eventpid = args }
|
226
|
+
event.should == 'woo'
|
227
|
+
eventpid.should be_an( Integer )
|
228
|
+
|
229
|
+
@conn.exec( 'UNLISTEN woo' )
|
230
|
+
|
231
|
+
Process.wait( pid )
|
232
|
+
end
|
233
|
+
|
234
|
+
it "doesn't collapse sequential notifications" do
|
235
|
+
@conn.exec( 'ROLLBACK' )
|
236
|
+
@conn.exec( 'LISTEN woo' )
|
237
|
+
@conn.exec( 'LISTEN war' )
|
238
|
+
@conn.exec( 'LISTEN woz' )
|
239
|
+
|
240
|
+
pid = fork do
|
241
|
+
begin
|
242
|
+
conn = PGconn.connect( @conninfo )
|
243
|
+
conn.exec( 'NOTIFY woo' )
|
244
|
+
conn.exec( 'NOTIFY war' )
|
245
|
+
conn.exec( 'NOTIFY woz' )
|
246
|
+
ensure
|
247
|
+
conn.finish
|
248
|
+
exit!
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
Process.wait( pid )
|
253
|
+
|
254
|
+
channels = []
|
255
|
+
3.times do
|
256
|
+
channels << @conn.wait_for_notify( 2 )
|
257
|
+
end
|
258
|
+
|
259
|
+
channels.should have( 3 ).members
|
260
|
+
channels.should include( 'woo', 'war', 'woz' )
|
261
|
+
|
262
|
+
@conn.exec( 'UNLISTEN woz' )
|
263
|
+
@conn.exec( 'UNLISTEN war' )
|
264
|
+
@conn.exec( 'UNLISTEN woo' )
|
265
|
+
end
|
266
|
+
|
267
|
+
it "returns notifications which are already in the queue before wait_for_notify is called " +
|
268
|
+
"without waiting for the socket to become readable" do
|
269
|
+
@conn.exec( 'ROLLBACK' )
|
270
|
+
@conn.exec( 'LISTEN woo' )
|
271
|
+
|
272
|
+
pid = fork do
|
273
|
+
begin
|
274
|
+
conn = PGconn.connect( @conninfo )
|
275
|
+
conn.exec( 'NOTIFY woo' )
|
276
|
+
ensure
|
277
|
+
conn.finish
|
278
|
+
exit!
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Wait for the forked child to send the notification
|
283
|
+
Process.wait( pid )
|
284
|
+
|
285
|
+
# Cause the notification to buffer, but not be read yet
|
286
|
+
@conn.exec( 'SELECT 1' )
|
287
|
+
|
288
|
+
@conn.wait_for_notify( 10 ).should == 'woo'
|
289
|
+
@conn.exec( 'UNLISTEN woo' )
|
290
|
+
end
|
291
|
+
|
292
|
+
it "yields the result if block is given to exec" do
|
293
|
+
rval = @conn.exec( "select 1234::int as a union select 5678::int as a" ) do |result|
|
294
|
+
values = []
|
295
|
+
result.should be_kind_of( PGresult )
|
296
|
+
result.ntuples.should == 2
|
297
|
+
result.each do |tuple|
|
298
|
+
values << tuple['a']
|
299
|
+
end
|
300
|
+
values
|
301
|
+
end
|
302
|
+
|
303
|
+
rval.should have( 2 ).members
|
304
|
+
rval.should include( '5678', '1234' )
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
it "correctly finishes COPY queries passed to #async_exec" do
|
309
|
+
@conn.async_exec( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" )
|
310
|
+
|
311
|
+
results = []
|
312
|
+
begin
|
313
|
+
data = @conn.get_copy_data( true )
|
314
|
+
if false == data
|
315
|
+
@conn.block( 2.0 )
|
316
|
+
data = @conn.get_copy_data( true )
|
317
|
+
end
|
318
|
+
results << data if data
|
319
|
+
end until data.nil?
|
320
|
+
|
321
|
+
results.should have( 2 ).members
|
322
|
+
results.should include( "1\n", "2\n" )
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
it "PGconn#block shouldn't block a second thread" do
|
327
|
+
t = Thread.new do
|
328
|
+
@conn.send_query( "select pg_sleep(3)" )
|
329
|
+
@conn.block
|
330
|
+
end
|
331
|
+
|
332
|
+
# :FIXME: There's a race here, but hopefully it's pretty small.
|
333
|
+
t.should be_alive()
|
334
|
+
|
335
|
+
@conn.cancel
|
336
|
+
t.join
|
337
|
+
end
|
338
|
+
|
339
|
+
it "PGconn#block should allow a timeout" do
|
340
|
+
@conn.send_query( "select pg_sleep(3)" )
|
341
|
+
|
342
|
+
start = Time.now
|
343
|
+
@conn.block( 0.1 )
|
344
|
+
finish = Time.now
|
345
|
+
|
346
|
+
(finish - start).should be_close( 0.1, 0.05 )
|
347
|
+
end
|
348
|
+
|
349
|
+
|
350
|
+
it "can encrypt a string given a password and username" do
|
351
|
+
PGconn.encrypt_password("postgres", "postgres").
|
352
|
+
should =~ /\S+/
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
it "raises an appropriate error if either of the required arguments for encrypt_password " +
|
357
|
+
"is not valid" do
|
358
|
+
expect {
|
359
|
+
PGconn.encrypt_password( nil, nil )
|
360
|
+
}.to raise_error( TypeError )
|
361
|
+
expect {
|
362
|
+
PGconn.encrypt_password( "postgres", nil )
|
363
|
+
}.to raise_error( TypeError )
|
364
|
+
expect {
|
365
|
+
PGconn.encrypt_password( nil, "postgres" )
|
366
|
+
}.to raise_error( TypeError )
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
it "allows fetching a column of values from a result by column number" do
|
371
|
+
res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
|
372
|
+
res.column_values( 0 ).should == %w[1 2 3]
|
373
|
+
res.column_values( 1 ).should == %w[2 3 4]
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
it "allows fetching a column of values from a result by field name" do
|
378
|
+
res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
|
379
|
+
res.field_values( 'column1' ).should == %w[1 2 3]
|
380
|
+
res.field_values( 'column2' ).should == %w[2 3 4]
|
381
|
+
end
|
382
|
+
|
383
|
+
|
384
|
+
it "raises an error if selecting an invalid column index" do
|
385
|
+
res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
|
386
|
+
expect {
|
387
|
+
res.column_values( 20 )
|
388
|
+
}.to raise_error( IndexError )
|
389
|
+
end
|
390
|
+
|
391
|
+
|
392
|
+
it "raises an error if selecting an invalid field name" do
|
393
|
+
res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
|
394
|
+
expect {
|
395
|
+
res.field_values( 'hUUuurrg' )
|
396
|
+
}.to raise_error( IndexError )
|
397
|
+
end
|
398
|
+
|
399
|
+
|
400
|
+
it "raises an error if column index is not a number" do
|
401
|
+
res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
|
402
|
+
expect {
|
403
|
+
res.column_values( 'hUUuurrg' )
|
404
|
+
}.to raise_error( TypeError )
|
405
|
+
end
|
406
|
+
|
407
|
+
|
408
|
+
it "can connect asynchronously" do
|
409
|
+
serv = TCPServer.new( '127.0.0.1', 54320 )
|
410
|
+
conn = PGconn.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
411
|
+
conn.connect_poll.should == PGconn::PGRES_POLLING_WRITING
|
412
|
+
select( nil, [IO.for_fd(conn.socket)], nil, 0.2 )
|
413
|
+
serv.close
|
414
|
+
if conn.connect_poll == PGconn::PGRES_POLLING_READING
|
415
|
+
select( [IO.for_fd(conn.socket)], nil, nil, 0.2 )
|
416
|
+
end
|
417
|
+
conn.connect_poll.should == PGconn::PGRES_POLLING_FAILED
|
418
|
+
end
|
419
|
+
|
420
|
+
it "discards previous results (if any) before waiting on an #async_exec"
|
421
|
+
|
422
|
+
it "calls the block if one is provided to #async_exec" do
|
423
|
+
result = nil
|
424
|
+
@conn.async_exec( "select 47 as one" ) do |pg_res|
|
425
|
+
result = pg_res[0]
|
426
|
+
end
|
427
|
+
result.should == { 'one' => '47' }
|
428
|
+
end
|
429
|
+
|
430
|
+
after( :each ) do
|
431
|
+
@conn.exec( 'ROLLBACK' )
|
432
|
+
end
|
433
|
+
|
434
|
+
after( :all ) do
|
435
|
+
teardown_testing_db( @conn )
|
436
|
+
end
|
437
|
+
end
|