em-pg-client 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,9 +17,7 @@ module PG
17
17
  @timer = ::EM::Timer.new(timeout) do
18
18
  detach
19
19
  @deferrable.protect do
20
- error = ConnectionBad.new("timeout expired (async)")
21
- error.instance_variable_set(:@connection, @client)
22
- raise error
20
+ @client.raise_error ConnectionBad, "timeout expired (async)"
23
21
  end
24
22
  end
25
23
  end
@@ -45,11 +43,7 @@ module PG
45
43
  @timer.cancel if @timer
46
44
  detach
47
45
  @deferrable.protect_and_succeed do
48
- unless polling_ok
49
- error = ConnectionBad.new(@client.error_message)
50
- error.instance_variable_set(:@connection, @client)
51
- raise error
52
- end
46
+ @client.raise_error ConnectionBad unless polling_ok
53
47
  @client.set_default_encoding unless reconnecting?
54
48
  @client
55
49
  end
@@ -17,17 +17,31 @@ module PG
17
17
  @is_connected
18
18
  end
19
19
 
20
- def watch_query(deferrable, send_proc)
21
- self.notify_readable = true
20
+ def one_result_mode?
21
+ @one_result_mode
22
+ end
23
+
24
+ def watch_results(deferrable, send_proc=nil, one_result_mode=false)
25
+ @one_result_mode = one_result_mode
22
26
  @last_result = nil
23
27
  @deferrable = deferrable
24
28
  @send_proc = send_proc
25
- @timer.cancel if @timer
26
- if (timeout = @client.query_timeout) > 0
27
- @notify_timestamp = Time.now
28
- setup_timer timeout
29
+ cancel_timer
30
+ if @client.is_busy
31
+ if @client.status == PG::CONNECTION_OK
32
+ self.notify_readable = true
33
+ if (timeout = @client.query_timeout) > 0
34
+ @notify_timestamp = Time.now
35
+ setup_timer timeout
36
+ end
37
+ else
38
+ @deferrable.protect do
39
+ @client.raise_error ConnectionBad
40
+ end
41
+ end
29
42
  else
30
- @timer = nil
43
+ self.notify_readable = true
44
+ fetch_results
31
45
  end
32
46
  self
33
47
  end
@@ -39,9 +53,7 @@ module PG
39
53
  self.notify_readable = false
40
54
  @client.async_command_aborted = true
41
55
  @deferrable.protect do
42
- error = ConnectionBad.new("query timeout expired (async)")
43
- error.instance_variable_set(:@connection, @client)
44
- raise error
56
+ @client.raise_error ConnectionBad, "query timeout expired (async)"
45
57
  end
46
58
  else
47
59
  setup_timer timeout, last_interval
@@ -56,20 +68,20 @@ module PG
56
68
  end
57
69
  end
58
70
 
59
- # Carefully extract the last result without
71
+ # Carefully extract results without
60
72
  # blocking the EventMachine reactor.
61
- def notify_readable
73
+ def fetch_results
62
74
  result = false
63
75
  @client.consume_input
64
76
  until @client.is_busy
65
- if (single_result = @client.get_result).nil?
66
- if (result = @last_result).nil?
67
- error = Error.new(@client.error_message)
68
- error.instance_variable_set(:@connection, @client)
69
- raise error
77
+ single_result = @client.blocking_get_result
78
+ if one_result_mode?
79
+ result = single_result
80
+ break
81
+ elsif single_result.nil?
82
+ if result = @last_result
83
+ result.check
70
84
  end
71
- result.check
72
- cancel_timer
73
85
  break
74
86
  end
75
87
  @last_result.clear if @last_result
@@ -78,22 +90,34 @@ module PG
78
90
  rescue Exception => e
79
91
  self.notify_readable = false
80
92
  cancel_timer
93
+ send_proc = @send_proc
94
+ @send_proc = nil
95
+ df = @deferrable
96
+ # prevent unbind error on auto re-connect
97
+ @deferrable = false
81
98
  if e.is_a?(PG::Error)
82
- @client.async_autoreconnect!(@deferrable, e, &@send_proc)
99
+ @client.async_autoreconnect!(df, e, &send_proc)
83
100
  else
84
- @deferrable.fail(e)
101
+ df.fail(e)
85
102
  end
86
103
  else
87
104
  if result == false
88
105
  @notify_timestamp = Time.now if @timer
89
106
  else
90
107
  self.notify_readable = false
108
+ cancel_timer
109
+ @send_proc = nil
91
110
  @deferrable.succeed(result)
92
111
  end
93
112
  end
94
113
 
114
+ alias_method :notify_readable, :fetch_results
115
+
95
116
  def unbind
96
117
  @is_connected = false
118
+ @deferrable.protect do
119
+ @client.raise_error ConnectionBad, "connection reset"
120
+ end if @deferrable
97
121
  end
98
122
  end
99
123
 
@@ -5,8 +5,10 @@ require 'date'
5
5
  require 'eventmachine'
6
6
  require 'pg/em'
7
7
 
8
- $pgserver_cmd_stop = %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" stop -s -m fast]
9
- $pgserver_cmd_start = %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" start -s -w]
8
+ $pgserver_cmd_stop = ENV['PG_CTL_STOP_CMD'] || %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" stop -s -m fast]
9
+ $pgserver_cmd_start = ENV['PG_CTL_START_CMD'] || %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" start -s -w]
10
+
11
+ DISCONNECTED_ERROR = ENV['PGHOST'].include?('/') ? PG::UnableToSend : PG::ConnectionBad
10
12
 
11
13
  shared_context 'pg-em common' do
12
14
  around(:each) do |testcase|
@@ -67,7 +69,7 @@ describe 'pg-em default autoreconnect' do
67
69
  it "should not get database size using query after server shutdown" do
68
70
  system($pgserver_cmd_stop).should be_true
69
71
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
70
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
72
+ ex.should be_an_instance_of DISCONNECTED_ERROR
71
73
  EM.stop
72
74
  end.should be_a_kind_of ::EM::DefaultDeferrable
73
75
  end
@@ -92,12 +94,47 @@ describe 'pg-em default autoreconnect' do
92
94
  system($pgserver_cmd_stop).should be_true
93
95
  system($pgserver_cmd_start).should be_true
94
96
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
95
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
97
+ ex.should be_an_instance_of DISCONNECTED_ERROR
96
98
  @tested_proc.call
97
99
  end.should be_a_kind_of ::EM::DefaultDeferrable
98
100
  end
99
101
  end
100
102
 
103
+ it "should fail to get last result asynchronously after server restart" do
104
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
105
+ system($pgserver_cmd_stop).should be_true
106
+ system($pgserver_cmd_start).should be_true
107
+ @client.get_last_result_defer do |ex|
108
+ ex.should be_an_instance_of PG::ConnectionBad
109
+ @client.status.should be PG::CONNECTION_OK
110
+ @client.get_last_result_defer do |result|
111
+ result.should be_nil
112
+ EM.stop
113
+ end
114
+ end.should be_a_kind_of EM::DefaultDeferrable
115
+ end
116
+
117
+ it "should fail to get each result asynchronously after server restart" do
118
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
119
+ system($pgserver_cmd_stop).should be_true
120
+ system($pgserver_cmd_start).should be_true
121
+ @client.get_result_defer do |result|
122
+ result.should be_an_instance_of PG::Result
123
+ expect do
124
+ result.check
125
+ end.to raise_error PG::Error
126
+ @client.status.should be PG::CONNECTION_OK
127
+ @client.get_result_defer do |ex|
128
+ ex.should be_an_instance_of PG::ConnectionBad
129
+ @client.status.should be PG::CONNECTION_OK
130
+ @client.get_result_defer do |result|
131
+ result.should be_nil
132
+ EM.stop
133
+ end.should be_a_kind_of ::EM::DefaultDeferrable
134
+ end.should be_a_kind_of EM::DefaultDeferrable
135
+ end
136
+ end
137
+
101
138
  before(:all) do
102
139
  @tested_proc = proc do
103
140
  @client.query_defer('SELECT pg_database_size(current_database());') do |result|
@@ -148,7 +185,7 @@ describe 'pg-em autoreconnect with on_autoreconnect' do
148
185
  system($pgserver_cmd_stop).should be_true
149
186
  system($pgserver_cmd_start).should be_true
150
187
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
151
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
188
+ ex.should be_an_instance_of DISCONNECTED_ERROR
152
189
  @tested_proc.call
153
190
  end.should be_a_kind_of ::EM::DefaultDeferrable
154
191
  end
@@ -159,7 +196,7 @@ describe 'pg-em autoreconnect with on_autoreconnect' do
159
196
  system($pgserver_cmd_stop).should be_true
160
197
  system($pgserver_cmd_start).should be_true
161
198
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
162
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
199
+ ex.should be_an_instance_of DISCONNECTED_ERROR
163
200
  EM.stop
164
201
  end.should be_a_kind_of ::EM::DefaultDeferrable
165
202
  end
@@ -209,6 +246,45 @@ describe 'pg-em autoreconnect with on_autoreconnect' do
209
246
  end.should be_a_kind_of ::EM::DefaultDeferrable
210
247
  end
211
248
 
249
+ it "should fail to get last result asynchronously after server restart" do
250
+ @client.on_autoreconnect = proc { true }
251
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
252
+ system($pgserver_cmd_stop).should be_true
253
+ system($pgserver_cmd_start).should be_true
254
+ @client.get_last_result_defer do |ex|
255
+ ex.should be_an_instance_of PG::ConnectionBad
256
+ @client.status.should be PG::CONNECTION_OK
257
+ @client.get_last_result_defer do |result|
258
+ result.should be_nil
259
+ EM.stop
260
+ end
261
+ end.should be_a_kind_of EM::DefaultDeferrable
262
+ end
263
+
264
+ it "should fail to get each result asynchronously after server restart" do
265
+ @client.on_autoreconnect = proc {
266
+ EM::DefaultDeferrable.new.tap {|df| df.succeed }
267
+ }
268
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
269
+ system($pgserver_cmd_stop).should be_true
270
+ system($pgserver_cmd_start).should be_true
271
+ @client.get_result_defer do |result|
272
+ result.should be_an_instance_of PG::Result
273
+ expect do
274
+ result.check
275
+ end.to raise_error PG::Error
276
+ @client.status.should be PG::CONNECTION_OK
277
+ @client.get_result_defer do |ex|
278
+ ex.should be_an_instance_of PG::ConnectionBad
279
+ @client.status.should be PG::CONNECTION_OK
280
+ @client.get_result_defer do |result|
281
+ result.should be_nil
282
+ EM.stop
283
+ end.should be_a_kind_of ::EM::DefaultDeferrable
284
+ end.should be_a_kind_of EM::DefaultDeferrable
285
+ end
286
+ end
287
+
212
288
  before(:all) do
213
289
  @tested_proc = proc do
214
290
  @client.exec_prepared_defer('get_db_size') do |result|
@@ -240,20 +316,76 @@ describe 'pg-em with autoreconnect disabled' do
240
316
  system($pgserver_cmd_stop).should be_true
241
317
  system($pgserver_cmd_start).should be_true
242
318
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
243
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
319
+ ex.should be_an_instance_of DISCONNECTED_ERROR
320
+ @client.status.should be PG::CONNECTION_BAD
244
321
  EM.stop
245
322
  end.should be_a_kind_of ::EM::DefaultDeferrable
246
323
  end
247
324
 
248
325
  it "should get database size using query after async manual connection reset" do
249
326
  @client.status.should be PG::CONNECTION_BAD
250
- @client.async_reset do |conn|
327
+ @client.reset_defer do |conn|
251
328
  conn.should be @client
252
329
  @client.status.should be PG::CONNECTION_OK
253
330
  @tested_proc.call
254
331
  end.should be_a_kind_of ::EM::DefaultDeferrable
255
332
  end
256
333
 
334
+ it "should fail to get last result asynchronously after server restart" do
335
+ system($pgserver_cmd_stop).should be_true
336
+ system($pgserver_cmd_start).should be_true
337
+ begin
338
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
339
+ rescue PG::UnableToSend
340
+ end
341
+ @client.get_last_result_defer do |ex|
342
+ ex.should be_an_instance_of PG::ConnectionBad
343
+ @client.status.should be PG::CONNECTION_BAD
344
+ @client.get_last_result_defer do |ex|
345
+ ex.should be_an_instance_of PG::ConnectionBad
346
+ @client.reset_defer do |conn|
347
+ conn.should be @client
348
+ @client.status.should be PG::CONNECTION_OK
349
+ EM.stop
350
+ end.should be_a_kind_of ::EM::DefaultDeferrable
351
+ end
352
+ end.should be_a_kind_of EM::DefaultDeferrable
353
+ end
354
+
355
+ it "should fail to get each result asynchronously after server restart" do
356
+ check_get_result = proc do
357
+ @client.get_result_defer do |ex|
358
+ ex.should be_an_instance_of PG::ConnectionBad
359
+ @client.status.should be PG::CONNECTION_BAD
360
+ @client.reset_defer do |conn|
361
+ conn.should be @client
362
+ @client.status.should be PG::CONNECTION_OK
363
+ EM.stop
364
+ end.should be_a_kind_of ::EM::DefaultDeferrable
365
+ end.should be_a_kind_of EM::DefaultDeferrable
366
+ end
367
+ system($pgserver_cmd_stop).should be_true
368
+ system($pgserver_cmd_start).should be_true
369
+ begin
370
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
371
+ rescue PG::UnableToSend
372
+ @client.get_result_defer do |result|
373
+ result.should be_an_instance_of PG::ConnectionBad
374
+ @client.status.should be PG::CONNECTION_BAD
375
+ check_get_result.call
376
+ end
377
+ else
378
+ @client.get_result_defer do |result|
379
+ result.should be_an_instance_of PG::Result
380
+ expect do
381
+ result.check
382
+ end.to raise_error PG::Error
383
+ @client.status.should be PG::CONNECTION_OK
384
+ check_get_result.call
385
+ end
386
+ end
387
+ end
388
+
257
389
  before(:all) do
258
390
  @tested_proc = proc do
259
391
  @client.query_defer('SELECT pg_database_size(current_database());') do |result|
@@ -106,6 +106,43 @@ end
106
106
 
107
107
  shared_context 'em-pg common after' do
108
108
 
109
+ if described_class.single_row_mode?
110
+
111
+ it "should get each result in single row mode" do
112
+ @client.single_row_mode?.should be_true
113
+ @client.get_result_defer do |result|
114
+ result.should be_nil
115
+ @client.send_query('SELECT data, id FROM foo order by id')
116
+ @client.set_single_row_mode
117
+ EM::Iterator.new(@values, 1).map(proc{ |(data, id), iter|
118
+ @client.get_result_defer do |result|
119
+ result.should be_an_instance_of PG::Result
120
+ result.check
121
+ result.result_status.should eq PG::PGRES_SINGLE_TUPLE
122
+ value = result.to_a
123
+ value.should eq [{'data' => data, 'id' => id.to_s}]
124
+ result.clear
125
+ iter.return value
126
+ end.should be_a_kind_of ::EM::DefaultDeferrable
127
+ }, proc{ |results|
128
+ results.length.should eq @values.length
129
+ @client.get_result_defer do |result|
130
+ result.should be_an_instance_of PG::Result
131
+ result.check
132
+ result.result_status.should eq PG::PGRES_TUPLES_OK
133
+ result.to_a.should eq []
134
+ result.clear
135
+ @client.get_result_defer do |result|
136
+ result.should be_nil
137
+ EM.stop
138
+ end.should be_a_kind_of ::EM::DefaultDeferrable
139
+ end.should be_a_kind_of ::EM::DefaultDeferrable
140
+ })
141
+ end.should be_a_kind_of ::EM::DefaultDeferrable
142
+ end
143
+
144
+ end
145
+
109
146
  it_should_execute("create prepared statement",
110
147
  :prepare_defer, 'get_foo', 'SELECT * FROM foo order by id')
111
148
 
@@ -282,4 +319,44 @@ shared_context 'em-pg common after' do
282
319
  end
283
320
  end
284
321
  end
322
+
323
+ it "should get last result asynchronously" do
324
+ @client.get_last_result_defer do |result|
325
+ result.should be_nil
326
+ @client.send_query('SELECT 1; SELECT 2; SELECT 3')
327
+ @client.get_last_result_defer do |result|
328
+ result.should be_an_instance_of PG::Result
329
+ result.getvalue(0,0).should eq '3'
330
+ result.clear
331
+ @client.get_last_result_defer do |result|
332
+ result.should be_nil
333
+ EM.stop
334
+ end
335
+ end.should be_a_kind_of ::EM::DefaultDeferrable
336
+ end.should be_a_kind_of ::EM::DefaultDeferrable
337
+ end
338
+
339
+ it "should get each result asynchronously" do
340
+ @client.get_result_defer do |result|
341
+ result.should be_nil
342
+ @client.send_query('SELECT 4; SELECT 5; SELECT 6')
343
+ EM::Iterator.new(%w[4 5 6], 1).map(proc{ |value, iter|
344
+ @client.get_result_defer do |result|
345
+ result.should be_an_instance_of PG::Result
346
+ result.check
347
+ result.result_status.should eq PG::PGRES_TUPLES_OK
348
+ result.getvalue(0,0).should eq value
349
+ result.clear
350
+ iter.return value
351
+ end.should be_a_kind_of ::EM::DefaultDeferrable
352
+ }, proc{ |results|
353
+ results.should eq %w[4 5 6]
354
+ @client.get_result_defer do |result|
355
+ result.should be_nil
356
+ EM.stop
357
+ end.should be_a_kind_of ::EM::DefaultDeferrable
358
+ })
359
+ end.should be_a_kind_of ::EM::DefaultDeferrable
360
+ end
361
+
285
362
  end
@@ -113,6 +113,33 @@ describe PG::EM::Client do
113
113
  ).should be_an_instance_of PG::Result
114
114
  end
115
115
 
116
+ if described_class.single_row_mode?
117
+
118
+ it "should get each result in single row mode" do
119
+ @client.single_row_mode?.should be_true
120
+ @client.send_query('SELECT data, id FROM foo order by id')
121
+ @client.set_single_row_mode
122
+ @values.each do |data, id|
123
+ result = @client.get_result
124
+ result.should be_an_instance_of PG::Result
125
+ result.result_status.should eq PG::PGRES_SINGLE_TUPLE
126
+ result.check
127
+ value = result.to_a
128
+ result.clear
129
+ value.should eq [{'data' => data, 'id' => id.to_s}]
130
+ end
131
+ result = @client.get_result
132
+ result.should be_an_instance_of PG::Result
133
+ result.check
134
+ result.result_status.should eq PG::PGRES_TUPLES_OK
135
+ result.to_a.should eq []
136
+ result.clear
137
+ @client.get_result.should be_nil
138
+ EM.stop
139
+ end
140
+
141
+ end
142
+
116
143
  it "should connect to database asynchronously" do
117
144
  this = :first
118
145
  Encoding.default_internal = Encoding::ISO_8859_1
@@ -149,12 +176,12 @@ describe PG::EM::Client do
149
176
  f = Fiber.current
150
177
  Fiber.new do
151
178
  begin
152
- described_class.new do |conn|
153
- this = :second
154
- Encoding.default_internal = nil
155
- conn.should be_an_instance_of described_class
156
- conn.external_encoding.should be conn.internal_encoding
157
- end
179
+ conn = described_class.new
180
+ this = :second
181
+ Encoding.default_internal = nil
182
+ conn.should be_an_instance_of described_class
183
+ conn.external_encoding.should be conn.internal_encoding
184
+ conn.finish
158
185
  ensure
159
186
  f.resume
160
187
  end
@@ -256,18 +283,59 @@ describe PG::EM::Client do
256
283
 
257
284
  it "should not expire after executing erraneous query" do
258
285
  @client.query_timeout.should eq 0
259
- @client.query_timeout = 0.1
260
- @client.query_timeout.should eq 0.1
286
+ @client.query_timeout = 1
287
+ @client.query_timeout.should eq 1
261
288
  expect {
262
289
  @client.query('SELLECT 1')
263
290
  }.to raise_error(PG::SyntaxError, /syntax error/)
264
291
  @client.async_command_aborted.should be_false
265
292
  @client.status.should be PG::CONNECTION_OK
266
- ::EM::Synchrony.sleep 0.11
293
+ ::EM::Synchrony.sleep 1.5
267
294
  @client.async_command_aborted.should be_false
268
295
  @client.status.should be PG::CONNECTION_OK
269
296
  end
270
297
 
298
+ it "should get last result asynchronously" do
299
+ result = @client.get_last_result
300
+ result.should be_nil
301
+ @client.get_last_result.should be_nil
302
+ @client.send_query('SELECT 1; SELECT 2; SELECT 3')
303
+ asynchronous = false
304
+ EM.next_tick { asynchronous = true }
305
+ result = @client.get_last_result
306
+ result.should be_an_instance_of PG::Result
307
+ result.getvalue(0,0).should eq '3'
308
+ result.clear
309
+ @client.get_last_result.should be_nil
310
+ asynchronous.should be true
311
+ end
312
+
313
+ it "should get each result asynchronously" do
314
+ result = @client.get_result
315
+ result.should be_nil
316
+ @client.get_result do |result|
317
+ result.should be_nil
318
+ end.should be_nil
319
+ @client.send_query('SELECT 4,pg_sleep(0.1); SELECT 5; SELECT 6')
320
+ asynchronous = false
321
+ EM.next_tick { asynchronous = true }
322
+ %w[4 5 6].map do |value|
323
+ result = @client.get_result do |result|
324
+ result.should be_an_instance_of PG::Result
325
+ result.check
326
+ result.result_status.should eq PG::PGRES_TUPLES_OK
327
+ result.getvalue(0,0).should eq value
328
+ result
329
+ end
330
+ expect do
331
+ result.clear
332
+ end.to raise_error PG::Error, /cleared/
333
+ value
334
+ end.should eq %w[4 5 6]
335
+ @client.get_result.should be_nil
336
+ asynchronous.should be true
337
+ end
338
+
271
339
  describe 'PG::EM::Client#transaction' do
272
340
 
273
341
  it "should raise ArgumentError when there is no block" do